mirror of
https://github.com/golang/go.git
synced 2026-01-30 07:32:05 +03:00
Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e64356a448 | ||
|
|
35a519b6b2 | ||
|
|
ba6496fe19 | ||
|
|
5272a7c97c | ||
|
|
0b575b4fb0 | ||
|
|
72766093e6 | ||
|
|
f3ed8e61d9 | ||
|
|
fddc08f94a | ||
|
|
d66ace1bab | ||
|
|
4cabf6992e | ||
|
|
4cb22ee812 | ||
|
|
985804b427 | ||
|
|
2bbb57c9d4 | ||
|
|
c16e37eab9 | ||
|
|
2018d431c7 | ||
|
|
49073c579e | ||
|
|
81d995d9ee | ||
|
|
842a1e78c9 | ||
|
|
2f73eedd42 | ||
|
|
b7b6e8e294 | ||
|
|
cf54026495 | ||
|
|
66b8787f6b | ||
|
|
cd951aeec4 | ||
|
|
951dbb1e5b | ||
|
|
fa325ea2a5 | ||
|
|
84b070f6fa | ||
|
|
17a492fdd5 | ||
|
|
f262eed960 | ||
|
|
8a6cd7a082 | ||
|
|
8c8a881688 | ||
|
|
0c07603272 | ||
|
|
de964716b3 | ||
|
|
5af12aedbd | ||
|
|
44a4250a57 | ||
|
|
4b1d9c468b | ||
|
|
b17fd8e49d | ||
|
|
13fe59bfda | ||
|
|
5a6ab1ec3e | ||
|
|
c58577b6c9 | ||
|
|
a462e9fbe8 | ||
|
|
a055bb9370 | ||
|
|
fb86bbb315 | ||
|
|
94227d241b | ||
|
|
613adc6268 | ||
|
|
1dc1e7c346 | ||
|
|
7d1c61cde5 | ||
|
|
37f1dd97f7 | ||
|
|
f0c6230863 | ||
|
|
052d7c8074 | ||
|
|
6bbfea923e | ||
|
|
b7ff8ffa05 | ||
|
|
befa17c5f4 | ||
|
|
cc8838d645 | ||
|
|
67d2a1e066 | ||
|
|
4c8037b278 | ||
|
|
b719a5875e | ||
|
|
fb7b50c13c | ||
|
|
2f866fe20f | ||
|
|
2e65ef623b | ||
|
|
d7b402a49a | ||
|
|
450a918b6f | ||
|
|
1ae4e73c11 | ||
|
|
2e3989e04e | ||
|
|
c11853c09b | ||
|
|
44a66acc71 | ||
|
|
633d0c9765 | ||
|
|
994ebd3066 | ||
|
|
8eecefd58d | ||
|
|
ea9edabf44 | ||
|
|
bc30423263 | ||
|
|
55a5beb12c | ||
|
|
a5a92aa673 | ||
|
|
5c379a437e | ||
|
|
d76bd214ae | ||
|
|
bd2e28190d | ||
|
|
ed4f3f3134 |
33
CONTRIBUTORS
33
CONTRIBUTORS
@@ -76,6 +76,7 @@ Alan Donovan <adonovan@google.com>
|
||||
Alan Shreve <alan@inconshreveable.com>
|
||||
Albert Nigmatzianov <albertnigma@gmail.com>
|
||||
Albert Strasheim <fullung@gmail.com>
|
||||
Albert Teoh <albert.teoh@gmail.com>
|
||||
Albert Yu <yukinying@gmail.com>
|
||||
Alberto Bertogli <albertito@blitiri.com.ar>
|
||||
Alberto Donizetti <alb.donizetti@gmail.com>
|
||||
@@ -140,6 +141,7 @@ Ali Rizvi-Santiago <arizvisa@gmail.com>
|
||||
Aliaksandr Valialkin <valyala@gmail.com>
|
||||
Alif Rachmawadi <subosito@gmail.com>
|
||||
Allan Simon <allan.simon@supinfo.com>
|
||||
Allen Li <ayatane@google.com>
|
||||
Alok Menghrajani <alok.menghrajani@gmail.com>
|
||||
Aman Gupta <aman@tmm1.net>
|
||||
Amir Mohammad Saied <amir@gluegadget.com>
|
||||
@@ -147,6 +149,7 @@ Amr Mohammed <merodiro@gmail.com>
|
||||
Amrut Joshi <amrut.joshi@gmail.com>
|
||||
Anand K. Mistry <anand@mistry.ninja>
|
||||
Anders Pearson <anders@columbia.edu>
|
||||
Anderson Queiroz <contato@andersonq.eti.br>
|
||||
André Carvalho <asantostc@gmail.com>
|
||||
Andre Nathan <andrenth@gmail.com>
|
||||
Andrea Nodari <andrea.nodari91@gmail.com>
|
||||
@@ -182,6 +185,7 @@ Andrew Radev <andrey.radev@gmail.com>
|
||||
Andrew Skiba <skibaa@gmail.com>
|
||||
Andrew Stribblehill <ads@wompom.org>
|
||||
Andrew Szeto <andrew@jabagawee.com>
|
||||
Andrew Todd <andrew.todd@wework.com>
|
||||
Andrew Werner <andrew@upthere.com> <awerner32@gmail.com>
|
||||
Andrew Wilkins <axwalk@gmail.com>
|
||||
Andrew Williams <williams.andrew@gmail.com>
|
||||
@@ -235,6 +239,7 @@ Arnaud Ysmal <arnaud.ysmal@gmail.com>
|
||||
Arne Hormann <arnehormann@gmail.com>
|
||||
Arnout Engelen <arnout@bzzt.net>
|
||||
Aron Nopanen <aron.nopanen@gmail.com>
|
||||
Artem Kolin <artemkaxboy@gmail.com>
|
||||
Arthur Fabre <arthur@arthurfabre.com>
|
||||
Arthur Khashaev <arthur@khashaev.ru>
|
||||
Artyom Pervukhin <artyom.pervukhin@gmail.com>
|
||||
@@ -284,6 +289,7 @@ Benny Siegert <bsiegert@gmail.com>
|
||||
Benoit Sigoure <tsunanet@gmail.com>
|
||||
Berengar Lehr <Berengar.Lehr@gmx.de>
|
||||
Berkant Ipek <41230766+0xbkt@users.noreply.github.com>
|
||||
Bharath Thiruveedula <tbharath91@gmail.com>
|
||||
Bill Neubauer <wcn@golang.org> <wcn@google.com> <bill.neubauer@gmail.com>
|
||||
Bill O'Farrell <billo@ca.ibm.com>
|
||||
Bill Prin <waprin@google.com>
|
||||
@@ -401,6 +407,7 @@ Chris Zou <chriszou@ca.ibm.com>
|
||||
Christian Alexander <christian@linux.com>
|
||||
Christian Couder <chriscool@tuxfamily.org>
|
||||
Christian Himpel <chressie@googlemail.com> <chressie@gmail.com>
|
||||
Christian Muehlhaeuser <muesli@gmail.com>
|
||||
Christian Pellegrin <chri@evolware.org>
|
||||
Christian R. Petrin <christianpetrin@gmail.com>
|
||||
Christine Hansmann <chhansmann@gmail.com>
|
||||
@@ -481,6 +488,7 @@ Daria Kolistratova <daria.kolistratova@intel.com>
|
||||
Darien Raymond <admin@v2ray.com>
|
||||
Darren Elwood <darren@textnode.com>
|
||||
Darren Grant <darren.e.grant@gmail.com>
|
||||
Darren McCleary <darren.rmc@gmail.com>
|
||||
Darshan Parajuli <parajulidarshan@gmail.com>
|
||||
Datong Sun <dndx@idndx.com>
|
||||
Dave Borowitz <dborowitz@google.com>
|
||||
@@ -501,6 +509,7 @@ David Chase <drchase@google.com>
|
||||
David Covert <davidhcovert@gmail.com>
|
||||
David Crawshaw <david.crawshaw@zentus.com> <crawshaw@google.com> <crawshaw@golang.org>
|
||||
David du Colombier <0intro@gmail.com>
|
||||
David Finkel <david.finkel@gmail.com>
|
||||
David Forsythe <dforsythe@gmail.com>
|
||||
David G. Andersen <dave.andersen@gmail.com>
|
||||
David Glasser <glasser@meteor.com>
|
||||
@@ -594,6 +603,7 @@ Dustin Shields-Cloues <dcloues@gmail.com>
|
||||
Dvir Volk <dvir@everything.me> <dvirsky@gmail.com>
|
||||
Dylan Waits <dylan@waits.io>
|
||||
Edan Bedrik <3d4nb3@gmail.com>
|
||||
Eddie Scholtz <escholtz@google.com>
|
||||
Eden Li <eden.li@gmail.com>
|
||||
Eduard Urbach <e.urbach@gmail.com>
|
||||
Eduardo Ramalho <eduardo.ramalho@gmail.com>
|
||||
@@ -763,9 +773,12 @@ GitHub User @pityonline (438222) <pityonline@gmail.com>
|
||||
GitHub User @pytimer (17105586) <lixin20101023@gmail.com>
|
||||
GitHub User @saitarunreddy (21041941) <saitarunreddypalla@gmail.com>
|
||||
GitHub User @shogo-ma (9860598) <Choroma194@gmail.com>
|
||||
GitHub User @tatsumack (4510569) <tatsu.mack@gmail.com>
|
||||
GitHub User @tell-k (26263) <ffk2005@gmail.com>
|
||||
GitHub User @uhei (2116845) <uhei@users.noreply.github.com>
|
||||
GitHub User @uropek (39370426) <uropek@gmail.com>
|
||||
GitHub User @utkarsh-extc (53217283) <53217283+utkarsh-extc@users.noreply.github.com>
|
||||
GitHub User @yuanhh (1298735) <yuan415030@gmail.com>
|
||||
GitHub User @ZZMarquis (7624583) <zhonglingjian3821@163.com>
|
||||
Giulio Iotti <dullgiulio@gmail.com>
|
||||
Giulio Micheloni <giulio.micheloni@gmail.com>
|
||||
@@ -861,6 +874,7 @@ Igor Bernstein <igorbernstein@google.com>
|
||||
Igor Dolzhikov <bluesriverz@gmail.com>
|
||||
Igor Vashyst <ivashyst@gmail.com>
|
||||
Igor Zhilianin <igor.zhilianin@gmail.com>
|
||||
Illya Yalovyy <yalovoy@gmail.com>
|
||||
Ilya Tocar <ilya.tocar@intel.com>
|
||||
INADA Naoki <songofacandy@gmail.com>
|
||||
Inanc Gumus <m@inanc.io>
|
||||
@@ -905,6 +919,7 @@ James Clarke <jrtc27@jrtc27.com>
|
||||
James Cowgill <James.Cowgill@imgtec.com>
|
||||
James Craig Burley <james-github@burleyarch.com>
|
||||
James David Chalfant <james.chalfant@gmail.com>
|
||||
James Eady <jmeady@google.com>
|
||||
James Fysh <james.fysh@gmail.com>
|
||||
James Gray <james@james4k.com>
|
||||
James Hartig <fastest963@gmail.com>
|
||||
@@ -937,6 +952,7 @@ Jan Lehnardt <jan@apache.org>
|
||||
Jan Mercl <0xjnml@gmail.com> <befelemepeseveze@gmail.com>
|
||||
Jan Newmarch <jan.newmarch@gmail.com>
|
||||
Jan Pilzer <jan.pilzer@gmx.de>
|
||||
Jan Steinke <jan.steinke@gmail.com>
|
||||
Jan Ziak <0xe2.0x9a.0x9b@gmail.com>
|
||||
Jani Monoses <jani.monoses@ubuntu.com> <jani.monoses@gmail.com>
|
||||
Jannis Andrija Schnitzer <jannis@schnitzer.im>
|
||||
@@ -954,6 +970,7 @@ Jason Smale <jsmale@zendesk.com>
|
||||
Jason Travis <infomaniac7@gmail.com>
|
||||
Jason Wangsadinata <jwangsadinata@gmail.com>
|
||||
Javier Kohen <jkohen@google.com>
|
||||
Javier Revillas <jrevillas@massivedynamic.io>
|
||||
Javier Segura <javism@gmail.com>
|
||||
Jay Conrod <jayconrod@google.com>
|
||||
Jay Taylor <outtatime@gmail.com>
|
||||
@@ -1071,6 +1088,8 @@ Jordan Krage <jmank88@gmail.com>
|
||||
Jordan Lewis <jordanthelewis@gmail.com>
|
||||
Jordan Liggitt <liggitt@google.com>
|
||||
Jordan Rhee <jordanrh@microsoft.com>
|
||||
Jordi Martin <jordimartin@gmail.com>
|
||||
Jorge Araya <jorgejavieran@yahoo.com.mx>
|
||||
Jos Visser <josv@google.com>
|
||||
Jose Luis Vázquez González <josvazg@gmail.com>
|
||||
Joseph Bonneau <jcb@google.com>
|
||||
@@ -1114,6 +1133,7 @@ Justin Gracenin <jgracenin@gmail.com>
|
||||
Justin Li <git@justinli.net>
|
||||
Justin Nuß <nuss.justin@gmail.com>
|
||||
Justyn Temme <justyntemme@gmail.com>
|
||||
Kelly Heller <pestophagous@gmail.com>
|
||||
Kai Backman <kaib@golang.org>
|
||||
Kai Dong <dokia2357@gmail.com>
|
||||
Kai Trukenmüller <ktye78@gmail.com>
|
||||
@@ -1159,6 +1179,7 @@ Kenta Mori <zoncoen@gmail.com>
|
||||
Ketan Parmar <ketanbparmar@gmail.com>
|
||||
Kevin Ballard <kevin@sb.org>
|
||||
Kevin Burke <kev@inburke.com>
|
||||
Kevin Gillette <extemporalgenome@gmail.com>
|
||||
Kevin Kirsche <kev.kirsche@gmail.com>
|
||||
Kevin Klues <klueska@gmail.com> <klueska@google.com>
|
||||
Kevin Malachowski <chowski@google.com>
|
||||
@@ -1284,6 +1305,7 @@ Marius A. Eriksen <marius@grailbio.com>
|
||||
Marius Nuennerich <mnu@google.com>
|
||||
Mark Adams <mark@markadams.me>
|
||||
Mark Bucciarelli <mkbucc@gmail.com>
|
||||
Mark Glines <mark@glines.org>
|
||||
Mark Harrison <marhar@google.com>
|
||||
Mark Percival <m@mdp.im>
|
||||
Mark Pulford <mark@kyne.com.au>
|
||||
@@ -1480,6 +1502,7 @@ Muir Manders <muir@mnd.rs>
|
||||
Mura Li <mura_li@castech.com.tw>
|
||||
Mykhailo Lesyk <mikhail@lesyk.org>
|
||||
Nan Deng <monnand@gmail.com>
|
||||
Nao Yonashiro <owan.orisano@gmail.com>
|
||||
Naoki Kanatani <k12naoki@gmail.com>
|
||||
Nate Wilkinson <nathanwilk7@gmail.com>
|
||||
Nathan Cantelmo <n.cantelmo@gmail.com>
|
||||
@@ -1566,6 +1589,7 @@ Paolo Giarrusso <p.giarrusso@gmail.com>
|
||||
Paolo Martini <mrtnpaolo@gmail.com>
|
||||
Parker Moore <parkrmoore@gmail.com>
|
||||
Parminder Singh <parmsingh101@gmail.com>
|
||||
Pascal Dierich <pascal@pascaldierich.com>
|
||||
Pascal S. de Kloe <pascal@quies.net>
|
||||
Pat Moroney <pat@pat.email>
|
||||
Patrick Barker <barkerp@vmware.com>
|
||||
@@ -1658,6 +1682,7 @@ Prasanna Swaminathan <prasanna@mediamath.com>
|
||||
Prashant Varanasi <prashant@prashantv.com>
|
||||
Pravendra Singh <hackpravj@gmail.com>
|
||||
Preetam Jinka <pj@preet.am>
|
||||
Pure White <wu.purewhite@gmail.com>
|
||||
Qais Patankar <qaisjp@gmail.com>
|
||||
Qiuxuan Zhu <ilsh1022@gmail.com>
|
||||
Quan Tran <qeed.quan@gmail.com>
|
||||
@@ -1774,6 +1799,7 @@ Sad Pencil <qh06@qq.com>
|
||||
Sai Cheemalapati <saicheems@google.com>
|
||||
Sakeven Jiang <jc5930@sina.cn>
|
||||
Salmān Aljammāz <s@0x65.net>
|
||||
Sam Arnold <sarnold64@bloomberg.net>
|
||||
Sam Boyer <tech@samboyer.org>
|
||||
Sam Ding <samding@ca.ibm.com>
|
||||
Sam Hug <samuel.b.hug@gmail.com>
|
||||
@@ -1785,6 +1811,7 @@ Sami Pönkänen <sami.ponkanen@gmail.com>
|
||||
Samuel Kelemen <SCKelemen@users.noreply.github.com>
|
||||
Samuel Tan <samueltan@google.com>
|
||||
Samuele Pedroni <pedronis@lucediurna.net>
|
||||
Sander van Harmelen <sander@vanharmelen.nl>
|
||||
Sanjay Menakuru <balasanjay@gmail.com>
|
||||
Santhosh Kumar Tekuri <santhosh.tekuri@gmail.com>
|
||||
Sarah Adams <shadams@google.com>
|
||||
@@ -1814,6 +1841,7 @@ Sebastien Williams-Wynn <sebastien@cytora.com>
|
||||
Segev Finer <segev208@gmail.com>
|
||||
Seiji Takahashi <timaki.st@gmail.com>
|
||||
Sergei Skorobogatov <skorobo@rambler.ru>
|
||||
Sergei Zagurskii <gvozdoder@gmail.com>
|
||||
Sergey 'SnakE' Gromov <snake.scaly@gmail.com>
|
||||
Sergey Arseev <sergey.arseev@intel.com>
|
||||
Sergey Dobrodey <sergey.dobrodey@synesis.ru>
|
||||
@@ -1845,6 +1873,7 @@ Shijie Hao <haormj@gmail.com>
|
||||
Shinji Tanaka <shinji.tanaka@gmail.com>
|
||||
Shintaro Kaneko <kaneshin0120@gmail.com>
|
||||
Shivakumar GN <shivakumar.gn@gmail.com>
|
||||
Shivani Singhal <shivani.singhal2804@gmail.com>
|
||||
Shivansh Rai <shivansh@freebsd.org>
|
||||
Shubham Sharma <shubham.sha12@gmail.com>
|
||||
Shun Fan <sfan@google.com>
|
||||
@@ -1865,6 +1894,7 @@ StalkR <stalkr@stalkr.net>
|
||||
Stan Schwertly <stan@schwertly.com>
|
||||
Stanislav Afanasev <php.progger@gmail.com>
|
||||
Steeve Morin <steeve.morin@gmail.com>
|
||||
Stefan Baebler <sbaebler@outbrain.com>
|
||||
Stefan Nilsson <snilsson@nada.kth.se> <trolleriprofessorn@gmail.com>
|
||||
Stepan Shabalin <neverliberty@gmail.com>
|
||||
Stephan Renatus <srenatus@chef.io>
|
||||
@@ -1951,6 +1981,7 @@ Thomas Wanielista <tomwans@gmail.com>
|
||||
Thorben Krueger <thorben.krueger@gmail.com>
|
||||
Thordur Bjornsson <thorduri@secnorth.net>
|
||||
Tiago Queiroz <contato@tiago.eti.br>
|
||||
Tianon Gravi <admwiggin@gmail.com>
|
||||
Tilman Dilo <tilman.dilo@gmail.com>
|
||||
Tim Cooijmans <timcooijmans@gmail.com>
|
||||
Tim Cooper <tim.cooper@layeh.com>
|
||||
@@ -1991,6 +2022,7 @@ Tony Walker <walkert.uk@gmail.com>
|
||||
Tooru Takahashi <tooru.takahashi134@gmail.com>
|
||||
Tor Andersson <tor.andersson@gmail.com>
|
||||
Tormod Erevik Lea <tormodlea@gmail.com>
|
||||
Toshihiro Shiino <shiino.toshihiro@gmail.com>
|
||||
Toshiki Shima <hayabusa1419@gmail.com>
|
||||
Totoro W <tw19881113@gmail.com>
|
||||
Travis Bischel <travis.bischel@gmail.com>
|
||||
@@ -2052,6 +2084,7 @@ Volker Dobler <dr.volker.dobler@gmail.com>
|
||||
Volodymyr Paprotski <vpaprots@ca.ibm.com>
|
||||
W. Trevor King <wking@tremily.us>
|
||||
Wade Simmons <wade@wades.im>
|
||||
Wagner Riffel <wgrriffel@gmail.com>
|
||||
Walter Poupore <wpoupore@google.com>
|
||||
Wander Lairson Costa <wcosta@mozilla.com>
|
||||
Warren Fernandes <warren.f.fernandes@gmail.com>
|
||||
|
||||
@@ -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.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>
|
||||
<li><a href="/doc/go1.10">Go 1.10</a> <small>(February 2018)</small></li>
|
||||
|
||||
@@ -23,6 +23,37 @@ in supported releases as needed by issuing minor revisions
|
||||
(for example, Go 1.6.1, Go 1.6.2, and so on).
|
||||
</p>
|
||||
|
||||
<h2 id="go1.13">go1.13 (released 2019/09/03)</h2>
|
||||
|
||||
<p>
|
||||
Go 1.13 is a major release of Go.
|
||||
Read the <a href="/doc/go1.13">Go 1.13 Release Notes</a> for more information.
|
||||
</p>
|
||||
|
||||
<h3 id="go1.13.minor">Minor revisions</h3>
|
||||
|
||||
<p>
|
||||
go1.13.1 (released 2019/09/25) includes security fixes to the
|
||||
<code>net/http</code> and <code>net/textproto</code> packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.13.1">Go
|
||||
1.13.1 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.13.2 (released 2019/10/17) includes security fixes to the
|
||||
<code>crypto/dsa</code> package and the compiler.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.13.2">Go
|
||||
1.13.2 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.13.3 (released 2019/10/17) includes fixes to the go command,
|
||||
the toolchain, the runtime, <code>syscall</code>, <code>net</code>,
|
||||
<code>net/http</code>, and <code>crypto/ecdsa</code> packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.13.3">Go
|
||||
1.13.3 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.12">go1.12 (released 2019/02/25)</h2>
|
||||
|
||||
<p>
|
||||
@@ -98,6 +129,27 @@ See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.12.9+labe
|
||||
1.12.9 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.12.10 (released 2019/09/25) includes security fixes to the
|
||||
<code>net/http</code> and <code>net/textproto</code> packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.12.10">Go
|
||||
1.12.10 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.12.11 (released 2019/10/17) includes security fixes to the
|
||||
<code>crypto/dsa</code> package.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.12.11">Go
|
||||
1.12.11 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.12.12 (released 2019/10/17) includes fixes to the go command,
|
||||
runtime, <code>syscall</code> and <code>net</code> packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.12.12">Go
|
||||
1.12.12 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.11">go1.11 (released 2018/08/24)</h2>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -12,7 +12,7 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
-->
|
||||
|
||||
<style>
|
||||
ul li { margin: 0.5em 0; }
|
||||
main ul li { margin: 0.5em 0; }
|
||||
</style>
|
||||
|
||||
<h2 id="introduction">Introduction to Go 1.10</h2>
|
||||
|
||||
@@ -12,7 +12,7 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
-->
|
||||
|
||||
<style>
|
||||
ul li { margin: 0.5em 0; }
|
||||
main ul li { margin: 0.5em 0; }
|
||||
</style>
|
||||
|
||||
<h2 id="introduction">Introduction to Go 1.11</h2>
|
||||
|
||||
@@ -12,7 +12,7 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
-->
|
||||
|
||||
<style>
|
||||
ul li { margin: 0.5em 0; }
|
||||
main ul li { margin: 0.5em 0; }
|
||||
</style>
|
||||
|
||||
<h2 id="introduction">Introduction to Go 1.12</h2>
|
||||
|
||||
126
doc/go1.13.html
126
doc/go1.13.html
@@ -12,16 +12,16 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
-->
|
||||
|
||||
<style>
|
||||
ul li { margin: 0.5em 0; }
|
||||
main ul li { margin: 0.5em 0; }
|
||||
</style>
|
||||
|
||||
<h2 id="introduction">DRAFT RELEASE NOTES - Introduction to Go 1.13</h2>
|
||||
<h2 id="introduction">Introduction to Go 1.13</h2>
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
Go 1.13 is not yet released. These are work-in-progress
|
||||
release notes. Go 1.13 is expected to be released in August 2019.
|
||||
</strong>
|
||||
The latest Go release, version 1.13, arrives six months after <a href="go1.12">Go 1.12</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>
|
||||
@@ -31,7 +31,8 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
for privacy information about these services and the
|
||||
<a href="/cmd/go/#hdr-Module_downloading_and_verification">go command documentation</a>
|
||||
for configuration details including how to disable the use of these servers or use
|
||||
different ones.
|
||||
different ones. If you depend on non-public modules, see the
|
||||
<a href="/cmd/go/#hdr-Module_configuration_for_non_public_modules">documentation for configuring your environment</a>.
|
||||
</p>
|
||||
|
||||
<h2 id="language">Changes to the language</h2>
|
||||
@@ -101,7 +102,7 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
|
||||
<h2 id="ports">Ports</h2>
|
||||
|
||||
<p>
|
||||
<p id="nacl">
|
||||
Go 1.13 is the last release that will run on Native Client (NaCl).
|
||||
</p>
|
||||
|
||||
@@ -121,7 +122,7 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
<h3 id="android">Android</h3>
|
||||
|
||||
<p><!-- CL 170127 -->
|
||||
Go programs are now compatible with Android Q.
|
||||
Go programs are now compatible with Android 10.
|
||||
</p>
|
||||
|
||||
<h3 id="darwin">Darwin</h3>
|
||||
@@ -138,7 +139,8 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
As <a href="go1.12#freebsd">announced</a> in the Go 1.12 release notes,
|
||||
Go 1.13 now requires FreeBSD 11.2 or later;
|
||||
support for previous versions has been discontinued.
|
||||
FreeBSD 12.0 or later requires a kernel with the COMPAT_FREEBSD11 option set (this is the default).
|
||||
FreeBSD 12.0 or later requires a kernel with the <code>COMPAT_FREEBSD11</code>
|
||||
option set (this is the default).
|
||||
</p>
|
||||
|
||||
<h3 id="illumos">Illumos</h3>
|
||||
@@ -149,18 +151,6 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
build tag.
|
||||
</p>
|
||||
|
||||
<h3 id="netbsd">NetBSD</h3>
|
||||
|
||||
<p><!--CL 155739 -->
|
||||
Go now supports NetBSD on arm64.
|
||||
</p>
|
||||
|
||||
<h3 id="openbsd">OpenBSD</h3>
|
||||
|
||||
<p><!--CL 174125 -->
|
||||
Go now supports OpenBSD on arm64.
|
||||
</p>
|
||||
|
||||
<h3 id="windows">Windows</h3>
|
||||
|
||||
<p><!-- CL 178977 -->
|
||||
@@ -315,7 +305,7 @@ go env -w GOSUMDB=off
|
||||
|
||||
<p>
|
||||
The <code>go</code> command now verifies the mapping
|
||||
between <a href="/cmd/go#hdr-Pseudo_versions">pseudo-versions</a> and
|
||||
between <a href="/cmd/go/#hdr-Pseudo_versions">pseudo-versions</a> and
|
||||
version-control metadata. Specifically:
|
||||
<ul>
|
||||
<li>The version prefix must be of the form <code>vX.0.0</code>, or derived
|
||||
@@ -551,9 +541,9 @@ godoc
|
||||
To support wrapping, <a href="#fmt"><code>fmt.Errorf</code></a> now has a <code>%w</code>
|
||||
verb for creating wrapped errors, and three new functions in
|
||||
the <a href="#errors"><code>errors</code></a> package (
|
||||
<a href="/pkg/errors#Unwrap"><code>errors.Unwrap</code></a>,
|
||||
<a href="/pkg/errors#Is"><code>errors.Is</code></a> and
|
||||
<a href="/pkg/errors#As"><code>errors.As</code></a>) simplify unwrapping
|
||||
<a href="/pkg/errors/#Unwrap"><code>errors.Unwrap</code></a>,
|
||||
<a href="/pkg/errors/#Is"><code>errors.Is</code></a> and
|
||||
<a href="/pkg/errors/#As"><code>errors.As</code></a>) simplify unwrapping
|
||||
and inspecting wrapped errors.
|
||||
</p>
|
||||
<p>
|
||||
@@ -592,10 +582,15 @@ godoc
|
||||
<dd>
|
||||
<p>
|
||||
Support for SSL version 3.0 (SSLv3) <a href="https://golang.org/issue/32716">
|
||||
is now deprecated and will be removed in Go 1.14</a>. Note that SSLv3
|
||||
<a href="https://tools.ietf.org/html/rfc7568">is cryptographically
|
||||
broken</a>, is already disabled by default in <code>crypto/tls</code>,
|
||||
and was never supported by Go clients.
|
||||
is now deprecated and will be removed in Go 1.14</a>. Note that SSLv3 is the
|
||||
<a href="https://tools.ietf.org/html/rfc7568">cryptographically broken</a>
|
||||
protocol predating TLS.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
SSLv3 was always disabled by default, other than in Go 1.12, when it was
|
||||
mistakenly enabled by default server-side. It is now again disabled by
|
||||
default. (SSLv3 was never supported client-side.)
|
||||
</p>
|
||||
|
||||
<p><!-- CL 177698 -->
|
||||
@@ -667,6 +662,24 @@ godoc
|
||||
|
||||
<dl id="fmt"><dt><a href="/pkg/fmt/">fmt</a></dt>
|
||||
<dd>
|
||||
<!-- CL 160245 -->
|
||||
<p>
|
||||
The printing verbs <code>%x</code> and <code>%X</code> now format floating-point and
|
||||
complex numbers in hexadecimal notation, in lower-case and upper-case respectively.
|
||||
</p>
|
||||
|
||||
<!-- CL 160246 -->
|
||||
<p>
|
||||
The new printing verb <code>%O</code> formats integers in base 8, emitting the <code>0o</code> prefix.
|
||||
</p>
|
||||
|
||||
<!-- CL 160247 -->
|
||||
<p>
|
||||
The scanner now accepts hexadecimal floating-point values, digit-separating underscores
|
||||
and leading <code>0b</code> and <code>0o</code> prefixes.
|
||||
See the <a href="#language">Changes to the language</a> for details.
|
||||
</p>
|
||||
|
||||
<!-- CL 176998 -->
|
||||
<p>The <a href="/pkg/fmt/#Errorf"><code>Errorf</code></a> function
|
||||
has a new verb, <code>%w</code>, whose operand must be an error.
|
||||
@@ -723,6 +736,18 @@ godoc
|
||||
The new <a href="/pkg/math/big/#Rat.SetUint64"><code>Rat.SetUint64</code></a> method sets the <code>Rat</code> to a <code>uint64</code> value.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 166157 -->
|
||||
For <a href="/pkg/math/big/#Float.Parse"><code>Float.Parse</code></a>, if base is 0, underscores
|
||||
may be used between digits for readability.
|
||||
See the <a href="#language">Changes to the language</a> for details.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 166157 -->
|
||||
For <a href="/pkg/math/big/#Int.SetString"><code>Int.SetString</code></a>, if base is 0, underscores
|
||||
may be used between digits for readability.
|
||||
See the <a href="#language">Changes to the language</a> for details.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 168237 -->
|
||||
<a href="/pkg/math/big/#Rat.SetString"><code>Rat.SetString</code></a> now accepts non-decimal floating point representations.
|
||||
</p>
|
||||
@@ -745,7 +770,7 @@ godoc
|
||||
<dl id="net"><dt><a href="/pkg/net/">net</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 156366 -->
|
||||
On Unix systems where <code>use-vc</code> is set in <code>resolve.conf</code>, TCP is used for DNS resolution.
|
||||
On Unix systems where <code>use-vc</code> is set in <code>resolv.conf</code>, TCP is used for DNS resolution.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 170678 -->
|
||||
@@ -760,7 +785,7 @@ godoc
|
||||
<code>Timeout</code> method that returns <code>true</code> if called.
|
||||
This can make a keep-alive error difficult to distinguish from
|
||||
an error returned due to a missed deadline as set by the
|
||||
<a href="/pkg/net#Conn"><code>SetDeadline</code></a>
|
||||
<a href="/pkg/net/#Conn"><code>SetDeadline</code></a>
|
||||
method and similar methods.
|
||||
Code that uses deadlines and checks for them with
|
||||
the <code>Timeout</code> method or
|
||||
@@ -789,13 +814,14 @@ godoc
|
||||
</p>
|
||||
|
||||
<p><!-- CL 140357 -->
|
||||
When reusing HTTP/2, the <a href="/pkg/net/http#Transport"><code>Transport</code></a> no longer performs unnecessary TLS handshakes.
|
||||
<a href="/pkg/net/http/#Transport.MaxConnsPerHost"><code>Transport.MaxConnsPerHost</code></a> now works
|
||||
properly with HTTP/2.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 154383 -->
|
||||
<a href="/pkg/net/http/#TimeoutHandler"><code>TimeoutHandler</code></a>'s
|
||||
<a href="/pkg/net/http/#ResponseWriter"><code>ResponseWriter</code></a> now implements the
|
||||
<a href="/pkg/net/http/#Pusher"><code>Pusher</code></a> and <a href="/pkg/net/http/#Flusher"><code>Flusher</code></a> interfaces.
|
||||
<a href="/pkg/net/http/#Pusher"><code>Pusher</code></a> interface.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 157339 -->
|
||||
@@ -813,14 +839,14 @@ godoc
|
||||
</p>
|
||||
|
||||
<p><!-- CL 167681 -->
|
||||
The new <a href="/pkg/net/http#Server"><code>Server</code></a> fields
|
||||
The new <a href="/pkg/net/http/#Server"><code>Server</code></a> fields
|
||||
<a href="/pkg/net/http/#Server.BaseContext"><code>BaseContext</code></a> and
|
||||
<a href="/pkg/net/http/#Server.ConnContext"><code>ConnContext</code></a>
|
||||
allow finer control over the <a href="/pkg/context#Context"><code>Context</code></a> values provided to requests and connections.
|
||||
allow finer control over the <a href="/pkg/context/#Context"><code>Context</code></a> values provided to requests and connections.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 167781 -->
|
||||
<a href="/pkg/net/http#DetectContentType"><code>http.DetectContentType</code></a> now correctly detects RAR signatures, and can now also detect RAR v5 signatures.
|
||||
<a href="/pkg/net/http/#DetectContentType"><code>http.DetectContentType</code></a> now correctly detects RAR signatures, and can now also detect RAR v5 signatures.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 173658 -->
|
||||
@@ -836,7 +862,8 @@ godoc
|
||||
</p>
|
||||
|
||||
<p><!-- CL 179457 -->
|
||||
<a href="/pkg/net/http/#Transport"><code>Transport</code></a> now silently ignores a <code>408 "Request Timeout"</code> response.
|
||||
The <a href="/pkg/net/http/#Transport"><code>Transport</code></a> no longer logs errors when servers
|
||||
gracefully shut down idle connections using a <code>"408 Request Timeout"</code> response.
|
||||
</p>
|
||||
|
||||
</dl><!-- net/http -->
|
||||
@@ -858,9 +885,9 @@ godoc
|
||||
<dl id="os/exec"><dt><a href="/pkg/os/exec/">os/exec</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 174318 -->
|
||||
On Windows, the environment for a <a href="/pkg/os/exec#Cmd"><code>Cmd</code></a> always inherits the
|
||||
On Windows, the environment for a <a href="/pkg/os/exec/#Cmd"><code>Cmd</code></a> always inherits the
|
||||
<code>%SYSTEMROOT%</code> value of the parent process unless the
|
||||
<a href="/pkg/os/exec#Cmd.Env"><code>Cmd.Env</code></a> field includes an explicit value for it.
|
||||
<a href="/pkg/os/exec/#Cmd.Env"><code>Cmd.Env</code></a> field includes an explicit value for it.
|
||||
</p>
|
||||
|
||||
</dl><!-- os/exec -->
|
||||
@@ -888,7 +915,19 @@ godoc
|
||||
|
||||
</dl><!-- runtime -->
|
||||
|
||||
<dl id="strings"><dt><a href="/pkg/strings">strings</a></dt>
|
||||
<dl id="strconv"><dt><a href="/pkg/strconv/">strconv</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 160243 -->
|
||||
For <a href="/pkg/strconv/#ParseFloat"><code>strconv.ParseFloat</code></a>,
|
||||
<a href="/pkg/strconv/#ParseInt"><code>strconv.ParseInt</code></a>
|
||||
and <a href="/pkg/strconv/#ParseUint"><code>strconv.ParseUint</code></a>,
|
||||
if base is 0, underscores may be used between digits for readability.
|
||||
See the <a href="#language">Changes to the language</a> for details.
|
||||
</p>
|
||||
|
||||
</dl><!-- strconv -->
|
||||
|
||||
<dl id="strings"><dt><a href="/pkg/strings/">strings</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 142003 -->
|
||||
The new <a href="/pkg/strings/#ToValidUTF8"><code>ToValidUTF8</code></a> function returns a
|
||||
@@ -942,9 +981,10 @@ godoc
|
||||
<dl id="syscall/js"><dt><a href="/pkg/syscall/js/">syscall/js</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 177537 -->
|
||||
TypedArrayOf has been replaced by
|
||||
<code>TypedArrayOf</code> has been replaced by
|
||||
<a href="/pkg/syscall/js/#CopyBytesToGo"><code>CopyBytesToGo</code></a> and
|
||||
<a href="/pkg/syscall/js/#CopyBytesToJS"><code>CopyBytesToJS</code></a> for copying bytes between a byte slice and a Uint8Array.
|
||||
<a href="/pkg/syscall/js/#CopyBytesToJS"><code>CopyBytesToJS</code></a> for copying bytes
|
||||
between a byte slice and a <code>Uint8Array</code>.
|
||||
</p>
|
||||
|
||||
</dl><!-- syscall/js -->
|
||||
|
||||
@@ -10,7 +10,7 @@ Edit .,s;^([a-z][A-Za-z0-9_/]+)\.([A-Z][A-Za-z0-9_]+\.)?([A-Z][A-Za-z0-9_]+)([ .
|
||||
-->
|
||||
|
||||
<style>
|
||||
ul li { margin: 0.5em 0; }
|
||||
main ul li { margin: 0.5em 0; }
|
||||
</style>
|
||||
|
||||
<h2 id="introduction">Introduction to Go 1.6</h2>
|
||||
|
||||
@@ -22,7 +22,7 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
-->
|
||||
|
||||
<style>
|
||||
ul li { margin: 0.5em 0; }
|
||||
main ul li { margin: 0.5em 0; }
|
||||
</style>
|
||||
|
||||
<h2 id="introduction">Introduction to Go 1.7</h2>
|
||||
|
||||
@@ -12,7 +12,7 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
-->
|
||||
|
||||
<style>
|
||||
ul li { margin: 0.5em 0; }
|
||||
main ul li { margin: 0.5em 0; }
|
||||
</style>
|
||||
|
||||
<h2 id="introduction">Introduction to Go 1.8</h2>
|
||||
|
||||
@@ -12,7 +12,7 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
-->
|
||||
|
||||
<style>
|
||||
ul li { margin: 0.5em 0; }
|
||||
main ul li { margin: 0.5em 0; }
|
||||
</style>
|
||||
|
||||
<h2 id="introduction">Introduction to Go 1.9</h2>
|
||||
|
||||
@@ -1330,7 +1330,7 @@ func checkLangCompat(lit *syntax.BasicLit) {
|
||||
}
|
||||
// len(s) > 2
|
||||
if strings.Contains(s, "_") {
|
||||
yyerror("underscores in numeric literals only supported as of -lang=go1.13")
|
||||
yyerrorv("go1.13", "underscores in numeric literals")
|
||||
return
|
||||
}
|
||||
if s[0] != '0' {
|
||||
@@ -1338,15 +1338,15 @@ func checkLangCompat(lit *syntax.BasicLit) {
|
||||
}
|
||||
base := s[1]
|
||||
if base == 'b' || base == 'B' {
|
||||
yyerror("binary literals only supported as of -lang=go1.13")
|
||||
yyerrorv("go1.13", "binary literals")
|
||||
return
|
||||
}
|
||||
if base == 'o' || base == 'O' {
|
||||
yyerror("0o/0O-style octal literals only supported as of -lang=go1.13")
|
||||
yyerrorv("go1.13", "0o/0O-style octal literals")
|
||||
return
|
||||
}
|
||||
if lit.Kind != syntax.IntLit && (base == 'x' || base == 'X') {
|
||||
yyerror("hexadecimal floating-point literals only supported as of -lang=go1.13")
|
||||
yyerrorv("go1.13", "hexadecimal floating-point literals")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +154,11 @@ func yyerrorl(pos src.XPos, format string, args ...interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
func yyerrorv(lang string, format string, args ...interface{}) {
|
||||
what := fmt.Sprintf(format, args...)
|
||||
yyerrorl(lineno, "%s requires %s or later (-lang was set to %s; check go.mod)", what, lang, flag_lang)
|
||||
}
|
||||
|
||||
func yyerror(format string, args ...interface{}) {
|
||||
yyerrorl(lineno, format, args...)
|
||||
}
|
||||
|
||||
@@ -632,7 +632,7 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||
return n
|
||||
}
|
||||
if t.IsSigned() && !langSupported(1, 13) {
|
||||
yyerror("invalid operation: %v (signed shift count type %v, only supported as of -lang=go1.13)", n, r.Type)
|
||||
yyerrorv("go1.13", "invalid operation: %v (signed shift count type %v)", n, r.Type)
|
||||
n.Type = nil
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -116,10 +116,10 @@ type posetNode struct {
|
||||
// the nodes are different, either because SetNonEqual was called before, or because
|
||||
// we know that they are strictly ordered.
|
||||
//
|
||||
// It is implemented as a forest of DAGs; in each DAG, if node A dominates B,
|
||||
// it means that A<B. Equality is represented by mapping two SSA values to the same
|
||||
// DAG node; when a new equality relation is recorded between two existing nodes,
|
||||
// the nodes are merged, adjusting incoming and outgoing edges.
|
||||
// It is implemented as a forest of DAGs; in each DAG, if there is a path (directed)
|
||||
// from node A to B, it means that A<B (or A<=B). Equality is represented by mapping
|
||||
// two SSA values to the same DAG node; when a new equality relation is recorded
|
||||
// between two existing nodes,the nodes are merged, adjusting incoming and outgoing edges.
|
||||
//
|
||||
// Constants are specially treated. When a constant is added to the poset, it is
|
||||
// immediately linked to other constants already present; so for instance if the
|
||||
@@ -519,11 +519,11 @@ func (po *poset) dfs(r uint32, strict bool, f func(i uint32) bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns true if i1 dominates i2.
|
||||
// Returns true if there is a path from i1 to i2.
|
||||
// If strict == true: if the function returns true, then i1 < i2.
|
||||
// If strict == false: if the function returns true, then i1 <= i2.
|
||||
// If the function returns false, no relation is known.
|
||||
func (po *poset) dominates(i1, i2 uint32, strict bool) bool {
|
||||
func (po *poset) reaches(i1, i2 uint32, strict bool) bool {
|
||||
return po.dfs(i1, strict, func(n uint32) bool {
|
||||
return n == i2
|
||||
})
|
||||
@@ -537,7 +537,7 @@ func (po *poset) findroot(i uint32) uint32 {
|
||||
// storing a bitset for each root using it as a mini bloom filter
|
||||
// of nodes present under that root.
|
||||
for _, r := range po.roots {
|
||||
if po.dominates(r, i, false) {
|
||||
if po.reaches(r, i, false) {
|
||||
return r
|
||||
}
|
||||
}
|
||||
@@ -560,7 +560,7 @@ func (po *poset) mergeroot(r1, r2 uint32) uint32 {
|
||||
// found, the function does not modify the DAG and returns false.
|
||||
func (po *poset) collapsepath(n1, n2 *Value) bool {
|
||||
i1, i2 := po.values[n1.ID], po.values[n2.ID]
|
||||
if po.dominates(i1, i2, true) {
|
||||
if po.reaches(i1, i2, true) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -796,7 +796,7 @@ func (po *poset) Ordered(n1, n2 *Value) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
return i1 != i2 && po.dominates(i1, i2, true)
|
||||
return i1 != i2 && po.reaches(i1, i2, true)
|
||||
}
|
||||
|
||||
// Ordered reports whether n1<=n2. It returns false either when it is
|
||||
@@ -814,8 +814,7 @@ func (po *poset) OrderedOrEqual(n1, n2 *Value) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
return i1 == i2 || po.dominates(i1, i2, false) ||
|
||||
(po.dominates(i2, i1, false) && !po.dominates(i2, i1, true))
|
||||
return i1 == i2 || po.reaches(i1, i2, false)
|
||||
}
|
||||
|
||||
// Equal reports whether n1==n2. It returns false either when it is
|
||||
@@ -923,8 +922,8 @@ func (po *poset) setOrder(n1, n2 *Value, strict bool) bool {
|
||||
// Both n1 and n2 are in the poset. This is the complex part of the algorithm
|
||||
// as we need to find many different cases and DAG shapes.
|
||||
|
||||
// Check if n1 somehow dominates n2
|
||||
if po.dominates(i1, i2, false) {
|
||||
// Check if n1 somehow reaches n2
|
||||
if po.reaches(i1, i2, false) {
|
||||
// This is the table of all cases we need to handle:
|
||||
//
|
||||
// DAG New Action
|
||||
@@ -935,7 +934,7 @@ func (po *poset) setOrder(n1, n2 *Value, strict bool) bool {
|
||||
// #4: N1<X<N2 | N1<N2 | do nothing
|
||||
|
||||
// Check if we're in case #2
|
||||
if strict && !po.dominates(i1, i2, true) {
|
||||
if strict && !po.reaches(i1, i2, true) {
|
||||
po.addchild(i1, i2, true)
|
||||
return true
|
||||
}
|
||||
@@ -944,8 +943,8 @@ func (po *poset) setOrder(n1, n2 *Value, strict bool) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if n2 somehow dominates n1
|
||||
if po.dominates(i2, i1, false) {
|
||||
// Check if n2 somehow reaches n1
|
||||
if po.reaches(i2, i1, false) {
|
||||
// This is the table of all cases we need to handle:
|
||||
//
|
||||
// DAG New Action
|
||||
@@ -1033,10 +1032,10 @@ func (po *poset) SetEqual(n1, n2 *Value) bool {
|
||||
|
||||
// If we already knew that n1<=n2, we can collapse the path to
|
||||
// record n1==n2 (and viceversa).
|
||||
if po.dominates(i1, i2, false) {
|
||||
if po.reaches(i1, i2, false) {
|
||||
return po.collapsepath(n1, n2)
|
||||
}
|
||||
if po.dominates(i2, i1, false) {
|
||||
if po.reaches(i2, i1, false) {
|
||||
return po.collapsepath(n2, n1)
|
||||
}
|
||||
|
||||
@@ -1084,10 +1083,10 @@ func (po *poset) SetNonEqual(n1, n2 *Value) bool {
|
||||
i1, f1 := po.lookup(n1)
|
||||
i2, f2 := po.lookup(n2)
|
||||
if f1 && f2 {
|
||||
if po.dominates(i1, i2, false) && !po.dominates(i1, i2, true) {
|
||||
if po.reaches(i1, i2, false) && !po.reaches(i1, i2, true) {
|
||||
po.addchild(i1, i2, true)
|
||||
}
|
||||
if po.dominates(i2, i1, false) && !po.dominates(i2, i1, true) {
|
||||
if po.reaches(i2, i1, false) && !po.reaches(i2, i1, true) {
|
||||
po.addchild(i2, i1, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ func TestPoset(t *testing.T) {
|
||||
{OrderedOrEqual, 4, 12},
|
||||
{OrderedOrEqual_Fail, 12, 4},
|
||||
{OrderedOrEqual, 4, 7},
|
||||
{OrderedOrEqual, 7, 4},
|
||||
{OrderedOrEqual_Fail, 7, 4},
|
||||
|
||||
// Dag #1: 1<4<=7<12
|
||||
{Checkpoint, 0, 0},
|
||||
@@ -450,7 +450,7 @@ func TestSetEqual(t *testing.T) {
|
||||
{SetOrderOrEqual, 20, 100},
|
||||
{SetOrder, 100, 110},
|
||||
{OrderedOrEqual, 10, 30},
|
||||
{OrderedOrEqual, 30, 10},
|
||||
{OrderedOrEqual_Fail, 30, 10},
|
||||
{Ordered_Fail, 10, 30},
|
||||
{Ordered_Fail, 30, 10},
|
||||
{Ordered, 10, 40},
|
||||
|
||||
@@ -191,6 +191,10 @@ func findPkgs(profiles []*Profile) (map[string]*Pkg, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if len(list) == 0 {
|
||||
return pkgs, nil
|
||||
}
|
||||
|
||||
// Note: usually run as "go tool cover" in which case $GOROOT is set,
|
||||
// in which case runtime.GOROOT() does exactly what we want.
|
||||
goTool := filepath.Join(runtime.GOROOT(), "bin/go")
|
||||
|
||||
@@ -5,7 +5,7 @@ go 1.12
|
||||
require (
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20180524225900-fc6590592b44 // indirect
|
||||
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045
|
||||
golang.org/x/arch v0.0.0-20190815191158-8a70ba74b3a1
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
|
||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 // indirect
|
||||
golang.org/x/tools v0.0.0-20190611154301-25a4f137592f
|
||||
|
||||
@@ -2,8 +2,8 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f h1:Jnx61latede7zDD3Di
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20180524225900-fc6590592b44 h1:pKqc8lAAA6rcwpvsephnRuZp4VHbfszZRClvqAE6Sq8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20180524225900-fc6590592b44/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045 h1:Pn8fQdvx+z1avAi7fdM2kRYWQNxGlavNDSyzrQg2SsU=
|
||||
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8=
|
||||
golang.org/x/arch v0.0.0-20190815191158-8a70ba74b3a1 h1:A71BZbKSu+DtCNry/x5JKn20C+64DirDHmePEA8k0FY=
|
||||
golang.org/x/arch v0.0.0-20190815191158-8a70ba74b3a1/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -17,3 +17,4 @@ 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-20190611154301-25a4f137592f h1:6awn5JC4pwVI5HiBqs7MDtRxnwV9PpO5iSA9v6P09pA=
|
||||
golang.org/x/tools v0.0.0-20190611154301-25a4f137592f/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
@@ -1018,7 +1018,6 @@
|
||||
// Dir string // absolute path to cached source root directory
|
||||
// Sum string // checksum for path, version (as in go.sum)
|
||||
// GoModSum string // checksum for go.mod (as in go.sum)
|
||||
// Latest bool // would @latest resolve to this version?
|
||||
// }
|
||||
//
|
||||
// See 'go help modules' for more about module queries.
|
||||
|
||||
@@ -164,8 +164,14 @@ var vcsGit = &vcsCmd{
|
||||
// See golang.org/issue/9032.
|
||||
tagSyncDefault: []string{"submodule update --init --recursive"},
|
||||
|
||||
scheme: []string{"git", "https", "http", "git+ssh", "ssh"},
|
||||
pingCmd: "ls-remote -- {scheme}://{repo}",
|
||||
scheme: []string{"git", "https", "http", "git+ssh", "ssh"},
|
||||
|
||||
// Leave out the '--' separator in the ls-remote command: git 2.7.4 does not
|
||||
// support such a separator for that command, and this use should be safe
|
||||
// without it because the {scheme} value comes from the predefined list above.
|
||||
// See golang.org/issue/33836.
|
||||
pingCmd: "ls-remote {scheme}://{repo}",
|
||||
|
||||
remoteRepo: gitRemoteRepo,
|
||||
}
|
||||
|
||||
@@ -898,7 +904,7 @@ func metaImportsForPrefix(importPrefix string, mod ModuleMode, security web.Secu
|
||||
}
|
||||
resp, err := web.Get(security, url)
|
||||
if err != nil {
|
||||
return setCache(fetchResult{url: url, err: fmt.Errorf("fetch %s: %v", resp.URL, err)})
|
||||
return setCache(fetchResult{url: url, err: fmt.Errorf("fetching %s: %v", importPrefix, err)})
|
||||
}
|
||||
body := resp.Body
|
||||
defer body.Close()
|
||||
@@ -907,7 +913,7 @@ func metaImportsForPrefix(importPrefix string, mod ModuleMode, security web.Secu
|
||||
return setCache(fetchResult{url: url, err: fmt.Errorf("parsing %s: %v", resp.URL, err)})
|
||||
}
|
||||
if len(imports) == 0 {
|
||||
err = fmt.Errorf("fetch %s: no go-import meta tag", url)
|
||||
err = fmt.Errorf("fetching %s: no go-import meta tag found in %s", importPrefix, resp.URL)
|
||||
}
|
||||
return setCache(fetchResult{url: url, imports: imports, err: err})
|
||||
})
|
||||
|
||||
@@ -1950,9 +1950,14 @@ func Packages(args []string) []*Package {
|
||||
// cannot be loaded at all.
|
||||
// The packages that fail to load will have p.Error != nil.
|
||||
func PackagesAndErrors(patterns []string) []*Package {
|
||||
if len(patterns) > 0 {
|
||||
for _, p := range patterns {
|
||||
if strings.HasSuffix(p, ".go") {
|
||||
for _, p := range patterns {
|
||||
// Listing is only supported with all patterns referring to either:
|
||||
// - Files that are part of the same directory.
|
||||
// - Explicit package paths or patterns.
|
||||
if strings.HasSuffix(p, ".go") {
|
||||
// We need to test whether the path is an actual Go file and not a
|
||||
// package path or pattern ending in '.go' (see golang.org/issue/34653).
|
||||
if fi, err := os.Stat(p); err == nil && !fi.IsDir() {
|
||||
return []*Package{GoFilesPackage(patterns)}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,10 +399,13 @@ func recompileForTest(pmain, preal, ptest, pxtest *Package) {
|
||||
}
|
||||
}
|
||||
|
||||
// Don't compile build info from a main package. This can happen
|
||||
// if -coverpkg patterns include main packages, since those packages
|
||||
// are imported by pmain. See golang.org/issue/30907.
|
||||
if p.Internal.BuildInfo != "" && p != pmain {
|
||||
// Force main packages the test imports to be built as libraries.
|
||||
// Normal imports of main packages are forbidden by the package loader,
|
||||
// but this can still happen if -coverpkg patterns include main packages:
|
||||
// covered packages are imported by pmain. Linking multiple packages
|
||||
// compiled with '-p main' causes duplicate symbol errors.
|
||||
// See golang.org/issue/30907, golang.org/issue/34114.
|
||||
if p.Name == "main" && p != pmain && p != ptest {
|
||||
split()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ corresponding to this Go struct:
|
||||
Dir string // absolute path to cached source root directory
|
||||
Sum string // checksum for path, version (as in go.sum)
|
||||
GoModSum string // checksum for go.mod (as in go.sum)
|
||||
Latest bool // would @latest resolve to this version?
|
||||
}
|
||||
|
||||
See 'go help modules' for more about module queries.
|
||||
@@ -66,7 +65,6 @@ type moduleJSON struct {
|
||||
Dir string `json:",omitempty"`
|
||||
Sum string `json:",omitempty"`
|
||||
GoModSum string `json:",omitempty"`
|
||||
Latest bool `json:",omitempty"`
|
||||
}
|
||||
|
||||
func runDownload(cmd *base.Command, args []string) {
|
||||
@@ -105,31 +103,6 @@ func runDownload(cmd *base.Command, args []string) {
|
||||
work.Add(m)
|
||||
}
|
||||
|
||||
latest := map[string]string{} // path → version
|
||||
if *downloadJSON {
|
||||
// We need to populate the Latest field, but if the main module depends on a
|
||||
// version newer than latest — or if the version requested on the command
|
||||
// line is itself newer than latest — that's not trivial to determine from
|
||||
// the info returned by ListModules. Instead, we issue a separate
|
||||
// ListModules request for "latest", which should be inexpensive relative to
|
||||
// downloading the modules.
|
||||
var latestArgs []string
|
||||
for _, m := range mods {
|
||||
if m.Error != "" {
|
||||
continue
|
||||
}
|
||||
latestArgs = append(latestArgs, m.Path+"@latest")
|
||||
}
|
||||
|
||||
if len(latestArgs) > 0 {
|
||||
for _, info := range modload.ListModules(latestArgs, listU, listVersions) {
|
||||
if info.Version != "" {
|
||||
latest[info.Path] = info.Version
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
work.Do(10, func(item interface{}) {
|
||||
m := item.(*moduleJSON)
|
||||
var err error
|
||||
@@ -160,9 +133,6 @@ func runDownload(cmd *base.Command, args []string) {
|
||||
m.Error = err.Error()
|
||||
return
|
||||
}
|
||||
if latest[m.Path] == m.Version {
|
||||
m.Latest = true
|
||||
}
|
||||
})
|
||||
|
||||
if *downloadJSON {
|
||||
|
||||
@@ -241,13 +241,6 @@ func (r *gitRepo) findRef(hash string) (ref string, ok bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
func unshallow(gitDir string) []string {
|
||||
if _, err := os.Stat(filepath.Join(gitDir, "shallow")); err == nil {
|
||||
return []string{"--unshallow"}
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// minHashDigits is the minimum number of digits to require
|
||||
// before accepting a hex digit sequence as potentially identifying
|
||||
// a specific commit in a git repo. (Of course, users can always
|
||||
@@ -397,29 +390,27 @@ func (r *gitRepo) stat(rev string) (*RevInfo, error) {
|
||||
// fetchRefsLocked requires that r.mu remain locked for the duration of the call.
|
||||
func (r *gitRepo) fetchRefsLocked() error {
|
||||
if r.fetchLevel < fetchAll {
|
||||
if err := r.fetchUnshallow("refs/heads/*:refs/heads/*", "refs/tags/*:refs/tags/*"); err != nil {
|
||||
// NOTE: To work around a bug affecting Git clients up to at least 2.23.0
|
||||
// (2019-08-16), we must first expand the set of local refs, and only then
|
||||
// unshallow the repository as a separate fetch operation. (See
|
||||
// golang.org/issue/34266 and
|
||||
// https://github.com/git/git/blob/4c86140027f4a0d2caaa3ab4bd8bfc5ce3c11c8a/transport.c#L1303-L1309.)
|
||||
|
||||
if _, err := Run(r.dir, "git", "fetch", "-f", r.remote, "refs/heads/*:refs/heads/*", "refs/tags/*:refs/tags/*"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(filepath.Join(r.dir, "shallow")); err == nil {
|
||||
if _, err := Run(r.dir, "git", "fetch", "--unshallow", "-f", r.remote); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
r.fetchLevel = fetchAll
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *gitRepo) fetchUnshallow(refSpecs ...string) error {
|
||||
// To work around a protocol version 2 bug that breaks --unshallow,
|
||||
// add -c protocol.version=0.
|
||||
// TODO(rsc): The bug is believed to be server-side, meaning only
|
||||
// on Google's Git servers. Once the servers are fixed, drop the
|
||||
// protocol.version=0. See Google-internal bug b/110495752.
|
||||
var protoFlag []string
|
||||
unshallowFlag := unshallow(r.dir)
|
||||
if len(unshallowFlag) > 0 {
|
||||
protoFlag = []string{"-c", "protocol.version=0"}
|
||||
}
|
||||
_, err := Run(r.dir, "git", protoFlag, "fetch", unshallowFlag, "-f", r.remote, refSpecs)
|
||||
return err
|
||||
}
|
||||
|
||||
// statLocal returns a RevInfo describing rev in the local git repository.
|
||||
// It uses version as info.Version.
|
||||
func (r *gitRepo) statLocal(version, rev string) (*RevInfo, error) {
|
||||
@@ -539,39 +530,10 @@ func (r *gitRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[s
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
var refs []string
|
||||
var protoFlag []string
|
||||
var unshallowFlag []string
|
||||
for _, tag := range redo {
|
||||
refs = append(refs, "refs/tags/"+tag+":refs/tags/"+tag)
|
||||
}
|
||||
if len(refs) > 1 {
|
||||
unshallowFlag = unshallow(r.dir)
|
||||
if len(unshallowFlag) > 0 {
|
||||
// To work around a protocol version 2 bug that breaks --unshallow,
|
||||
// add -c protocol.version=0.
|
||||
// TODO(rsc): The bug is believed to be server-side, meaning only
|
||||
// on Google's Git servers. Once the servers are fixed, drop the
|
||||
// protocol.version=0. See Google-internal bug b/110495752.
|
||||
protoFlag = []string{"-c", "protocol.version=0"}
|
||||
}
|
||||
}
|
||||
if _, err := Run(r.dir, "git", protoFlag, "fetch", unshallowFlag, "-f", r.remote, refs); err != nil {
|
||||
if err := r.fetchRefsLocked(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(bcmills): after the 1.11 freeze, replace the block above with:
|
||||
// if r.fetchLevel <= fetchSome {
|
||||
// r.fetchLevel = fetchSome
|
||||
// var refs []string
|
||||
// for _, tag := range redo {
|
||||
// refs = append(refs, "refs/tags/"+tag+":refs/tags/"+tag)
|
||||
// }
|
||||
// if _, err := Run(r.dir, "git", "fetch", "--update-shallow", "-f", r.remote, refs); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// }
|
||||
|
||||
if _, err := r.readFileRevs(redo, file, files); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -338,10 +338,10 @@ var codeRepoTests = []codeRepoTest{
|
||||
vcs: "git",
|
||||
path: "gopkg.in/yaml.v2",
|
||||
rev: "v2",
|
||||
version: "v2.2.3-0.20190319135612-7b8349ac747c",
|
||||
name: "7b8349ac747c6a24702b762d2c4fd9266cf4f1d6",
|
||||
short: "7b8349ac747c",
|
||||
time: time.Date(2019, 03, 19, 13, 56, 12, 0, time.UTC),
|
||||
version: "v2.2.5-0.20191002202810-970885f01c8b",
|
||||
name: "970885f01c8bc1fecb7ab1c8ce8e7609bda45530",
|
||||
short: "970885f01c8b",
|
||||
time: time.Date(2019, 10, 2, 20, 28, 10, 0, time.UTC),
|
||||
gomod: "module \"gopkg.in/yaml.v2\"\n\nrequire (\n\t\"gopkg.in/check.v1\" v0.0.0-20161208181325-20d25e280405\n)\n",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ var importTests = []struct {
|
||||
},
|
||||
{
|
||||
path: "golang.org/x/net",
|
||||
err: "module golang.org/x/net@.* found, but does not contain package golang.org/x/net",
|
||||
err: `module golang.org/x/net@.* found \(v0.0.0-.*\), but does not contain package golang.org/x/net`,
|
||||
},
|
||||
{
|
||||
path: "golang.org/x/text",
|
||||
|
||||
@@ -1140,7 +1140,7 @@ func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
|
||||
if mpath := f.Module.Mod.Path; mpath != origPath && mpath != mod.Path {
|
||||
return nil, module.VersionError(mod, fmt.Errorf(`parsing go.mod:
|
||||
module declares its path as: %s
|
||||
but was required as: %s`, mod.Path, mpath))
|
||||
but was required as: %s`, mpath, mod.Path))
|
||||
}
|
||||
if f.Go != nil {
|
||||
r.versions.LoadOrStore(mod, f.Go.Version)
|
||||
@@ -1205,6 +1205,11 @@ func (*mvsReqs) next(m module.Version) (module.Version, error) {
|
||||
return module.Version{Path: m.Path, Version: "none"}, nil
|
||||
}
|
||||
|
||||
// fetch downloads the given module (or its replacement)
|
||||
// and returns its location.
|
||||
//
|
||||
// The isLocal return value reports whether the replacement,
|
||||
// if any, is local to the filesystem.
|
||||
func fetch(mod module.Version) (dir string, isLocal bool, err error) {
|
||||
if mod == Target {
|
||||
return ModRoot(), true, nil
|
||||
|
||||
@@ -381,9 +381,10 @@ func QueryPattern(pattern, query string, allowed func(module.Version) bool) ([]Q
|
||||
r.Packages = match(r.Mod, root, isLocal)
|
||||
if len(r.Packages) == 0 {
|
||||
return r, &PackageNotInModuleError{
|
||||
Mod: r.Mod,
|
||||
Query: query,
|
||||
Pattern: pattern,
|
||||
Mod: r.Mod,
|
||||
Replacement: Replacement(r.Mod),
|
||||
Query: query,
|
||||
Pattern: pattern,
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
@@ -471,7 +472,17 @@ func queryPrefixModules(candidateModules []string, queryModule func(path string)
|
||||
notExistErr = rErr
|
||||
}
|
||||
} else if err == nil {
|
||||
err = r.err
|
||||
if len(found) > 0 || noPackage != nil {
|
||||
// golang.org/issue/34094: If we have already found a module that
|
||||
// could potentially contain the target package, ignore unclassified
|
||||
// errors for modules with shorter paths.
|
||||
|
||||
// golang.org/issue/34383 is a special case of this: if we have
|
||||
// already found example.com/foo/v2@v2.0.0 with a matching go.mod
|
||||
// file, ignore the error from example.com/foo@v2.0.0.
|
||||
} else {
|
||||
err = r.err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -526,21 +537,32 @@ func (e *NoMatchingVersionError) Error() string {
|
||||
// code for the versions it knows about, and thus did not have the opportunity
|
||||
// to return a non-400 status code to suppress fallback.
|
||||
type PackageNotInModuleError struct {
|
||||
Mod module.Version
|
||||
Query string
|
||||
Pattern string
|
||||
Mod module.Version
|
||||
Replacement module.Version
|
||||
Query string
|
||||
Pattern string
|
||||
}
|
||||
|
||||
func (e *PackageNotInModuleError) Error() string {
|
||||
found := ""
|
||||
if e.Query != e.Mod.Version {
|
||||
if r := e.Replacement; r.Path != "" {
|
||||
replacement := r.Path
|
||||
if r.Version != "" {
|
||||
replacement = fmt.Sprintf("%s@%s", r.Path, r.Version)
|
||||
}
|
||||
if e.Query == e.Mod.Version {
|
||||
found = fmt.Sprintf(" (replaced by %s)", replacement)
|
||||
} else {
|
||||
found = fmt.Sprintf(" (%s, replaced by %s)", e.Mod.Version, replacement)
|
||||
}
|
||||
} else if e.Query != e.Mod.Version {
|
||||
found = fmt.Sprintf(" (%s)", e.Mod.Version)
|
||||
}
|
||||
|
||||
if strings.Contains(e.Pattern, "...") {
|
||||
return fmt.Sprintf("module %s@%s%s found, but does not contain packages matching %s", e.Mod.Path, e.Query, found, e.Pattern)
|
||||
return fmt.Sprintf("module %s@%s found%s, but does not contain packages matching %s", e.Mod.Path, e.Query, found, e.Pattern)
|
||||
}
|
||||
return fmt.Sprintf("module %s@%s%s found, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern)
|
||||
return fmt.Sprintf("module %s@%s found%s, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern)
|
||||
}
|
||||
|
||||
// ModuleHasRootPackage returns whether module m contains a package m.Path.
|
||||
|
||||
@@ -363,30 +363,40 @@ func ImportPathsQuiet(patterns []string) []*Match {
|
||||
|
||||
// CleanPatterns returns the patterns to use for the given
|
||||
// command line. It canonicalizes the patterns but does not
|
||||
// evaluate any matches.
|
||||
// evaluate any matches. It preserves text after '@' for commands
|
||||
// that accept versions.
|
||||
func CleanPatterns(patterns []string) []string {
|
||||
if len(patterns) == 0 {
|
||||
return []string{"."}
|
||||
}
|
||||
var out []string
|
||||
for _, a := range patterns {
|
||||
var p, v string
|
||||
if i := strings.IndexByte(a, '@'); i < 0 {
|
||||
p = a
|
||||
} else {
|
||||
p = a[:i]
|
||||
v = a[i:]
|
||||
}
|
||||
|
||||
// Arguments are supposed to be import paths, but
|
||||
// as a courtesy to Windows developers, rewrite \ to /
|
||||
// in command-line arguments. Handles .\... and so on.
|
||||
if filepath.Separator == '\\' {
|
||||
a = strings.ReplaceAll(a, `\`, `/`)
|
||||
p = strings.ReplaceAll(p, `\`, `/`)
|
||||
}
|
||||
|
||||
// Put argument in canonical form, but preserve leading ./.
|
||||
if strings.HasPrefix(a, "./") {
|
||||
a = "./" + path.Clean(a)
|
||||
if a == "./." {
|
||||
a = "."
|
||||
if strings.HasPrefix(p, "./") {
|
||||
p = "./" + path.Clean(p)
|
||||
if p == "./." {
|
||||
p = "."
|
||||
}
|
||||
} else {
|
||||
a = path.Clean(a)
|
||||
p = path.Clean(p)
|
||||
}
|
||||
out = append(out, a)
|
||||
|
||||
out = append(out, p+v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -572,8 +572,9 @@ func runTest(cmd *base.Command, args []string) {
|
||||
}
|
||||
|
||||
// Pass timeout to tests if it exists.
|
||||
// Prepend rather than appending so that it appears before positional arguments.
|
||||
if testActualTimeout > 0 {
|
||||
testArgs = append(testArgs, "-test.timeout="+testActualTimeout.String())
|
||||
testArgs = append([]string{"-test.timeout=" + testActualTimeout.String()}, testArgs...)
|
||||
}
|
||||
|
||||
// show passing test output (after buffering) with -v flag.
|
||||
|
||||
@@ -200,12 +200,12 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
|
||||
// same compiler settings and can reuse each other's results.
|
||||
// If not, the reason is already recorded in buildGcflags.
|
||||
fmt.Fprintf(h, "compile\n")
|
||||
// Only include the package directory if it may affect the output.
|
||||
// We trim workspace paths for all packages when -trimpath is set.
|
||||
// The compiler hides the exact value of $GOROOT
|
||||
// when building things in GOROOT,
|
||||
// but it does not hide the exact value of $GOPATH.
|
||||
// Include the full dir in that case.
|
||||
// when building things in GOROOT.
|
||||
// Assume b.WorkDir is being trimmed properly.
|
||||
if !p.Goroot && !strings.HasPrefix(p.Dir, b.WorkDir) {
|
||||
if !p.Goroot && !cfg.BuildTrimpath && !strings.HasPrefix(p.Dir, b.WorkDir) {
|
||||
fmt.Fprintf(h, "dir %s\n", p.Dir)
|
||||
}
|
||||
fmt.Fprintf(h, "goos %s goarch %s\n", cfg.Goos, cfg.Goarch)
|
||||
@@ -1030,7 +1030,7 @@ func (b *Builder) vet(a *Action) error {
|
||||
// dependency tree turn on *more* analysis, as here.
|
||||
// (The unsafeptr check does not write any facts for use by
|
||||
// later vet runs.)
|
||||
if a.Package.Goroot && !VetExplicit {
|
||||
if a.Package.Goroot && !VetExplicit && VetTool == "" {
|
||||
// Note that $GOROOT/src/buildall.bash
|
||||
// does the same for the misc-compile trybots
|
||||
// and should be updated if these flags are
|
||||
|
||||
@@ -441,10 +441,15 @@ func (ts *testScript) cmdCmp(neg bool, args []string) {
|
||||
// It would be strange to say "this file can have any content except this precise byte sequence".
|
||||
ts.fatalf("unsupported: ! cmp")
|
||||
}
|
||||
quiet := false
|
||||
if len(args) > 0 && args[0] == "-q" {
|
||||
quiet = true
|
||||
args = args[1:]
|
||||
}
|
||||
if len(args) != 2 {
|
||||
ts.fatalf("usage: cmp file1 file2")
|
||||
}
|
||||
ts.doCmdCmp(args, false)
|
||||
ts.doCmdCmp(args, false, quiet)
|
||||
}
|
||||
|
||||
// cmpenv compares two files with environment variable substitution.
|
||||
@@ -452,13 +457,18 @@ func (ts *testScript) cmdCmpenv(neg bool, args []string) {
|
||||
if neg {
|
||||
ts.fatalf("unsupported: ! cmpenv")
|
||||
}
|
||||
quiet := false
|
||||
if len(args) > 0 && args[0] == "-q" {
|
||||
quiet = true
|
||||
args = args[1:]
|
||||
}
|
||||
if len(args) != 2 {
|
||||
ts.fatalf("usage: cmpenv file1 file2")
|
||||
}
|
||||
ts.doCmdCmp(args, true)
|
||||
ts.doCmdCmp(args, true, quiet)
|
||||
}
|
||||
|
||||
func (ts *testScript) doCmdCmp(args []string, env bool) {
|
||||
func (ts *testScript) doCmdCmp(args []string, env, quiet bool) {
|
||||
name1, name2 := args[0], args[1]
|
||||
var text1, text2 string
|
||||
if name1 == "stdout" {
|
||||
@@ -484,7 +494,9 @@ func (ts *testScript) doCmdCmp(args []string, env bool) {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(&ts.log, "[diff -%s +%s]\n%s\n", name1, name2, diff(text1, text2))
|
||||
if !quiet {
|
||||
fmt.Fprintf(&ts.log, "[diff -%s +%s]\n%s\n", name1, name2, diff(text1, text2))
|
||||
}
|
||||
ts.fatalf("%s and %s differ", name1, name2)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
example.com/badchain/c v1.1.0
|
||||
|
||||
-- .mod --
|
||||
module example.com/badchain/wrong
|
||||
module badchain.example.com/c
|
||||
-- .info --
|
||||
{"Version":"v1.1.0"}
|
||||
-- c.go --
|
||||
|
||||
16
src/cmd/go/testdata/mod/example.com_dotgo.go_v1.0.0.txt
vendored
Normal file
16
src/cmd/go/testdata/mod/example.com_dotgo.go_v1.0.0.txt
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
This module's path ends with ".go".
|
||||
Based on github.com/nats-io/nats.go.
|
||||
Used in regression tests for golang.org/issue/32483.
|
||||
|
||||
-- .mod --
|
||||
module example.com/dotgo.go
|
||||
|
||||
go 1.13
|
||||
-- .info --
|
||||
{"Version":"v1.0.0"}
|
||||
-- go.mod --
|
||||
module example.com/dotgo.go
|
||||
|
||||
go 1.13
|
||||
-- dotgo.go --
|
||||
package dotgo
|
||||
29
src/cmd/go/testdata/script/build_trimpath.txt
vendored
29
src/cmd/go/testdata/script/build_trimpath.txt
vendored
@@ -1,21 +1,44 @@
|
||||
[short] skip
|
||||
|
||||
env -r GOROOT_REGEXP=$GOROOT
|
||||
env -r WORK_REGEXP=$WORK
|
||||
env -r WORK_REGEXP='$WORK' # don't expand $WORK; grep replaces $WORK in text before matching.
|
||||
env GOROOT GOROOT_REGEXP WORK WORK_REGEXP
|
||||
|
||||
# A binary built without -trimpath should contain the current workspace
|
||||
# and GOROOT for debugging and stack traces.
|
||||
cd a
|
||||
go build -o hello.exe hello.go
|
||||
grep -q $WORK_REGEXP hello.exe
|
||||
grep -q $GOROOT_REGEXP hello.exe
|
||||
|
||||
# A binary built with -trimpath should not contain the current workspace
|
||||
# or GOROOT.
|
||||
go build -trimpath -o hello.exe hello.go
|
||||
! grep -q $GOROOT_REGEXP hello.exe
|
||||
! grep -q $WORK_REGEXP hello.exe
|
||||
cd ..
|
||||
|
||||
# A binary from an external module built with -trimpath should not contain
|
||||
# the current workspace or GOROOT.
|
||||
env GO111MODULE=on
|
||||
go build -trimpath -o fortune.exe rsc.io/fortune
|
||||
! grep -q $GOROOT_REGEXP fortune.exe
|
||||
! grep -q $WORK_REGEXP fortune.exe
|
||||
|
||||
-- hello.go --
|
||||
# Two binaries built from identical packages in different directories
|
||||
# should be identical.
|
||||
mkdir b
|
||||
cp a/go.mod a/hello.go b
|
||||
cd a
|
||||
go build -trimpath -o ../a.exe .
|
||||
cd ../b
|
||||
go build -trimpath -o ../b.exe .
|
||||
cd ..
|
||||
cmp -q a.exe b.exe
|
||||
|
||||
-- a/hello.go --
|
||||
package main
|
||||
func main() { println("hello") }
|
||||
|
||||
-- go.mod --
|
||||
-- a/go.mod --
|
||||
module m
|
||||
|
||||
9
src/cmd/go/testdata/script/cover_mod_empty.txt
vendored
Normal file
9
src/cmd/go/testdata/script/cover_mod_empty.txt
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
go tool cover -func=cover.out
|
||||
stdout total.*statements.*0.0%
|
||||
|
||||
go mod init golang.org/issue/33855
|
||||
|
||||
go tool cover -func=cover.out
|
||||
stdout total.*statements.*0.0%
|
||||
|
||||
-- cover.out --
|
||||
@@ -1,29 +1,32 @@
|
||||
# This test checks that multiple main packages can be tested
|
||||
# with -coverpkg=all without duplicate symbol errors.
|
||||
# Verifies golang.org/issue/30374.
|
||||
|
||||
env GO111MODULE=on
|
||||
# Verifies golang.org/issue/30374, golang.org/issue/34114.
|
||||
|
||||
[short] skip
|
||||
cd $GOPATH/src/example.com/cov
|
||||
|
||||
env GO111MODULE=on
|
||||
go test -coverpkg=all ./...
|
||||
|
||||
-- go.mod --
|
||||
env GO111MODULE=off
|
||||
go test -coverpkg=all ./...
|
||||
|
||||
-- $GOPATH/src/example.com/cov/go.mod --
|
||||
module example.com/cov
|
||||
|
||||
-- mainonly/mainonly.go --
|
||||
-- $GOPATH/src/example.com/cov/mainonly/mainonly.go --
|
||||
package main
|
||||
|
||||
func main() {}
|
||||
|
||||
-- mainwithtest/mainwithtest.go --
|
||||
-- $GOPATH/src/example.com/cov/mainwithtest/mainwithtest.go --
|
||||
package main
|
||||
|
||||
func main() {}
|
||||
|
||||
func Foo() {}
|
||||
|
||||
-- mainwithtest/mainwithtest_test.go --
|
||||
-- $GOPATH/src/example.com/cov/mainwithtest/mainwithtest_test.go --
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
@@ -32,10 +35,10 @@ func TestFoo(t *testing.T) {
|
||||
Foo()
|
||||
}
|
||||
|
||||
-- xtest/x.go --
|
||||
-- $GOPATH/src/example.com/cov/xtest/x.go --
|
||||
package x
|
||||
|
||||
-- xtest/x_test.go --
|
||||
-- $GOPATH/src/example.com/cov/xtest/x_test.go --
|
||||
package x_test
|
||||
|
||||
import "testing"
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# golang.org/issue/29591: 'go get' was following plain-HTTP redirects even without -insecure.
|
||||
# golang.org/issue/34049: 'go get' would panic in case of an insecure redirect in GOPATH mode
|
||||
|
||||
[!net] skip
|
||||
[!exec:git] skip
|
||||
|
||||
env GO111MODULE=on
|
||||
env GOPROXY=direct
|
||||
env GOSUMDB=off
|
||||
env GO111MODULE=off
|
||||
|
||||
! go get -d vcs-test.golang.org/insecure/go/insecure
|
||||
stderr 'redirected .* to insecure URL'
|
||||
|
||||
37
src/cmd/go/testdata/script/list_ambiguous_path.txt
vendored
Normal file
37
src/cmd/go/testdata/script/list_ambiguous_path.txt
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# Ensures that we can correctly list package patterns ending in '.go'.
|
||||
# See golang.org/issue/34653.
|
||||
|
||||
# A single pattern for a package ending in '.go'.
|
||||
go list ./foo.go
|
||||
stdout '^test/foo.go$'
|
||||
|
||||
# Multiple patterns for packages including one ending in '.go'.
|
||||
go list ./bar ./foo.go
|
||||
stdout '^test/bar$'
|
||||
stdout '^test/foo.go$'
|
||||
|
||||
# A single pattern for a Go file.
|
||||
go list ./a.go
|
||||
stdout '^command-line-arguments$'
|
||||
|
||||
# A single typo-ed pattern for a Go file. This should
|
||||
# treat the wrong pattern as if it were a package.
|
||||
! go list ./foo.go/b.go
|
||||
stderr 'package ./foo.go/b.go: cannot find package "."'
|
||||
|
||||
# Multiple patterns for Go files with a typo. This should
|
||||
# treat the wrong pattern as if it were a non-existint file.
|
||||
! go list ./foo.go/a.go ./foo.go/b.go
|
||||
[windows] stderr './foo.go/b.go: The system cannot find the file specified'
|
||||
[!windows] stderr './foo.go/b.go: no such file or directory'
|
||||
|
||||
-- a.go --
|
||||
package main
|
||||
-- bar/a.go --
|
||||
package bar
|
||||
-- foo.go/a.go --
|
||||
package foo.go
|
||||
-- go.mod --
|
||||
module "test"
|
||||
|
||||
go 1.13
|
||||
25
src/cmd/go/testdata/script/list_split_main.txt
vendored
Normal file
25
src/cmd/go/testdata/script/list_split_main.txt
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# This test checks that a "main" package with an external test package
|
||||
# is recompiled only once.
|
||||
# Verifies golang.org/issue/34321.
|
||||
|
||||
env GO111MODULE=off
|
||||
|
||||
go list -e -test -deps -f '{{if not .Standard}}{{.ImportPath}}{{end}}' pkg
|
||||
cmp stdout want
|
||||
|
||||
-- $GOPATH/src/pkg/pkg.go --
|
||||
package main
|
||||
|
||||
func main() {}
|
||||
|
||||
-- $GOPATH/src/pkg/pkg_test.go --
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test(t *testing.T) {}
|
||||
|
||||
-- want --
|
||||
pkg
|
||||
pkg [pkg.test]
|
||||
pkg.test
|
||||
4
src/cmd/go/testdata/script/mod_download.txt
vendored
4
src/cmd/go/testdata/script/mod_download.txt
vendored
@@ -17,7 +17,6 @@ stderr 'this.domain.is.invalid'
|
||||
stdout '"Error": ".*this.domain.is.invalid.*"'
|
||||
|
||||
# download -json with version should print JSON
|
||||
# and download the .info file for the 'latest' version.
|
||||
go mod download -json 'rsc.io/quote@<=v1.5.0'
|
||||
stdout '^\t"Path": "rsc.io/quote"'
|
||||
stdout '^\t"Version": "v1.5.0"'
|
||||
@@ -28,14 +27,13 @@ stdout '^\t"Sum": "h1:6fJa6E\+wGadANKkUMlZ0DhXFpoKlslOQDCo259XtdIE="' # hash of
|
||||
stdout '^\t"GoModSum": "h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe\+TKr0="'
|
||||
! stdout '"Error"'
|
||||
|
||||
exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.info
|
||||
|
||||
# download queries above should not have added to go.mod.
|
||||
go list -m all
|
||||
! stdout rsc.io
|
||||
|
||||
# add to go.mod so we can test non-query downloads
|
||||
go mod edit -require rsc.io/quote@v1.5.2
|
||||
! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.info
|
||||
! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod
|
||||
! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
env GO111MODULE=on
|
||||
|
||||
# If the module is the latest version of itself,
|
||||
# the Latest field should be set.
|
||||
go mod download -json rsc.io/quote@v1.5.2
|
||||
stdout '"Latest":\s*true'
|
||||
|
||||
# If the module is older than latest, the field should be unset.
|
||||
go mod download -json rsc.io/quote@v1.5.1
|
||||
! stdout '"Latest":'
|
||||
|
||||
# If the module is newer than "latest", the field should be unset...
|
||||
go mod download -json rsc.io/quote@v1.5.3-pre1
|
||||
! stdout '"Latest":'
|
||||
|
||||
# ...even if that version is also what is required by the main module.
|
||||
go mod init example.com
|
||||
go mod edit -require rsc.io/quote@v1.5.3-pre1
|
||||
go mod download -json rsc.io/quote@v1.5.3-pre1
|
||||
! stdout '"Latest":'
|
||||
20
src/cmd/go/testdata/script/mod_get_direct.txt
vendored
Normal file
20
src/cmd/go/testdata/script/mod_get_direct.txt
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# Regression test for golang.org/issue/34092: with an empty module cache,
|
||||
# 'GOPROXY=direct go get golang.org/x/tools/gopls@master' did not correctly
|
||||
# resolve the pseudo-version for its dependency on golang.org/x/tools.
|
||||
|
||||
[short] skip
|
||||
[!net] skip
|
||||
[!exec:git] skip
|
||||
|
||||
env GO111MODULE=on
|
||||
env GOPROXY=direct
|
||||
env GOSUMDB=off
|
||||
|
||||
go list -m cloud.google.com/go@master
|
||||
! stdout 'v0.0.0-'
|
||||
|
||||
-- go.mod --
|
||||
module example.com
|
||||
|
||||
go 1.14
|
||||
-- go.sum --
|
||||
13
src/cmd/go/testdata/script/mod_get_insecure_redirect.txt
vendored
Normal file
13
src/cmd/go/testdata/script/mod_get_insecure_redirect.txt
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# golang.org/issue/29591: 'go get' was following plain-HTTP redirects even without -insecure.
|
||||
|
||||
[!net] skip
|
||||
[!exec:git] skip
|
||||
|
||||
env GO111MODULE=on
|
||||
env GOPROXY=direct
|
||||
env GOSUMDB=off
|
||||
|
||||
! go get -d vcs-test.golang.org/insecure/go/insecure
|
||||
stderr 'redirected .* to insecure URL'
|
||||
|
||||
go get -d -insecure vcs-test.golang.org/insecure/go/insecure
|
||||
23
src/cmd/go/testdata/script/mod_get_major.txt
vendored
Normal file
23
src/cmd/go/testdata/script/mod_get_major.txt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
[!net] skip
|
||||
[!exec:git] skip
|
||||
|
||||
env GO111MODULE=on
|
||||
env GOPROXY=direct
|
||||
env GOSUMDB=off
|
||||
|
||||
# golang.org/issue/34383: if a module path ends in a major-version suffix,
|
||||
# ensure that 'direct' mode can resolve the package to a module.
|
||||
|
||||
go get -d vcs-test.golang.org/git/v3pkg.git/v3@v3.0.0
|
||||
|
||||
go list -m vcs-test.golang.org/git/v3pkg.git/v3
|
||||
stdout '^vcs-test.golang.org/git/v3pkg.git/v3 v3.0.0$'
|
||||
|
||||
go get -d vcs-test.golang.org/git/empty-v2-without-v1.git/v2@v2.0.0
|
||||
|
||||
go list -m vcs-test.golang.org/git/empty-v2-without-v1.git/v2
|
||||
stdout '^vcs-test.golang.org/git/empty-v2-without-v1.git/v2 v2.0.0$'
|
||||
|
||||
-- go.mod --
|
||||
module example.com
|
||||
go 1.13
|
||||
@@ -10,11 +10,11 @@ grep 'require rsc.io/quote' go.mod
|
||||
|
||||
cp go.mod.orig go.mod
|
||||
! go get -d rsc.io/quote/x...
|
||||
stderr 'go get rsc.io/quote/x...: module rsc.io/quote@upgrade \(v1.5.2\) found, but does not contain packages matching rsc.io/quote/x...'
|
||||
stderr 'go get rsc.io/quote/x...: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x...'
|
||||
! grep 'require rsc.io/quote' go.mod
|
||||
|
||||
! go get -d rsc.io/quote/x/...
|
||||
stderr 'go get rsc.io/quote/x/...: module rsc.io/quote@upgrade \(v1.5.2\) found, but does not contain packages matching rsc.io/quote/x/...'
|
||||
stderr 'go get rsc.io/quote/x/...: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x/...'
|
||||
! grep 'require rsc.io/quote' go.mod
|
||||
|
||||
# If a pattern matches no packages within a module, the module should not
|
||||
|
||||
30
src/cmd/go/testdata/script/mod_get_trailing_slash.txt
vendored
Normal file
30
src/cmd/go/testdata/script/mod_get_trailing_slash.txt
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# go list should succeed to load a package ending with ".go" if the path does
|
||||
# not correspond to an existing local file. Listing a pattern ending with
|
||||
# ".go/" should try to list a package regardless of whether a file exists at the
|
||||
# path without the suffixed "/" or not.
|
||||
go list example.com/dotgo.go
|
||||
stdout ^example.com/dotgo.go$
|
||||
go list example.com/dotgo.go/
|
||||
stdout ^example.com/dotgo.go$
|
||||
|
||||
# go get -d should succeed in either case, with or without a version.
|
||||
# Arguments are interpreted as packages or package patterns with versions,
|
||||
# not source files.
|
||||
go get -d example.com/dotgo.go
|
||||
go get -d example.com/dotgo.go/
|
||||
go get -d example.com/dotgo.go@v1.0.0
|
||||
go get -d example.com/dotgo.go/@v1.0.0
|
||||
|
||||
# go get (without -d) should also succeed in either case.
|
||||
[short] skip
|
||||
go get example.com/dotgo.go
|
||||
go get example.com/dotgo.go/
|
||||
go get example.com/dotgo.go@v1.0.0
|
||||
go get example.com/dotgo.go/@v1.0.0
|
||||
|
||||
-- go.mod --
|
||||
module m
|
||||
|
||||
go 1.13
|
||||
|
||||
require example.com/dotgo.go v1.0.0
|
||||
20
src/cmd/go/testdata/script/mod_list_upgrade.txt
vendored
20
src/cmd/go/testdata/script/mod_list_upgrade.txt
vendored
@@ -1,28 +1,8 @@
|
||||
env GO111MODULE=on
|
||||
|
||||
# If the current version is not latest, 'go list -u' should include its upgrade.
|
||||
go list -m -u all
|
||||
stdout 'rsc.io/quote v1.2.0 \[v1\.5\.2\]'
|
||||
|
||||
# If the current version is latest, 'go list -u' should omit the upgrade.
|
||||
go get -d rsc.io/quote@v1.5.2
|
||||
go list -m -u all
|
||||
stdout 'rsc.io/quote v1.5.2$'
|
||||
|
||||
# If the current version is newer than latest, 'go list -u' should
|
||||
# omit the upgrade.
|
||||
go get -d rsc.io/quote@v1.5.3-pre1
|
||||
go list -m -u all
|
||||
stdout 'rsc.io/quote v1.5.3-pre1$'
|
||||
|
||||
# If the current build list has a higher version and the user asks about
|
||||
# a lower one, -u should report the upgrade for the lower one
|
||||
# but leave the build list unchanged.
|
||||
go list -m -u rsc.io/quote@v1.5.1
|
||||
stdout 'rsc.io/quote v1.5.1 \[v1.5.2\]$'
|
||||
go list -m -u rsc.io/quote
|
||||
stdout 'rsc.io/quote v1.5.3-pre1$'
|
||||
|
||||
-- go.mod --
|
||||
module x
|
||||
require rsc.io/quote v1.2.0
|
||||
|
||||
20
src/cmd/go/testdata/script/mod_load_badchain.txt
vendored
20
src/cmd/go/testdata/script/mod_load_badchain.txt
vendored
@@ -58,28 +58,28 @@ func Test(t *testing.T) {}
|
||||
-- update-main-expected --
|
||||
go get: example.com/badchain/c@v1.0.0 updating to
|
||||
example.com/badchain/c@v1.1.0: parsing go.mod:
|
||||
module declares its path as: example.com/badchain/c
|
||||
but was required as: example.com/badchain/wrong
|
||||
module declares its path as: badchain.example.com/c
|
||||
but was required as: example.com/badchain/c
|
||||
-- update-a-expected --
|
||||
go get: example.com/badchain/a@v1.1.0 requires
|
||||
example.com/badchain/b@v1.1.0 requires
|
||||
example.com/badchain/c@v1.1.0: parsing go.mod:
|
||||
module declares its path as: example.com/badchain/c
|
||||
but was required as: example.com/badchain/wrong
|
||||
module declares its path as: badchain.example.com/c
|
||||
but was required as: example.com/badchain/c
|
||||
-- list-expected --
|
||||
go: example.com/badchain/a@v1.1.0 requires
|
||||
example.com/badchain/b@v1.1.0 requires
|
||||
example.com/badchain/c@v1.1.0: parsing go.mod:
|
||||
module declares its path as: example.com/badchain/c
|
||||
but was required as: example.com/badchain/wrong
|
||||
module declares its path as: badchain.example.com/c
|
||||
but was required as: example.com/badchain/c
|
||||
-- list-missing-expected --
|
||||
go: m/use imports
|
||||
example.com/badchain/c: example.com/badchain/c@v1.1.0: parsing go.mod:
|
||||
module declares its path as: example.com/badchain/c
|
||||
but was required as: example.com/badchain/wrong
|
||||
module declares its path as: badchain.example.com/c
|
||||
but was required as: example.com/badchain/c
|
||||
-- list-missing-test-expected --
|
||||
go: m/testuse tested by
|
||||
m/testuse.test imports
|
||||
example.com/badchain/c: example.com/badchain/c@v1.1.0: parsing go.mod:
|
||||
module declares its path as: example.com/badchain/c
|
||||
but was required as: example.com/badchain/wrong
|
||||
module declares its path as: badchain.example.com/c
|
||||
but was required as: example.com/badchain/c
|
||||
|
||||
7
src/cmd/go/testdata/script/mod_replace.txt
vendored
7
src/cmd/go/testdata/script/mod_replace.txt
vendored
@@ -38,6 +38,13 @@ grep 'not-rsc.io/quote/v3 v3.1.0' go.mod
|
||||
exec ./a5.exe
|
||||
stdout 'Concurrency is not parallelism.'
|
||||
|
||||
# Error messages for modules not found in replacements should
|
||||
# indicate the replacement module.
|
||||
cp go.mod.orig go.mod
|
||||
go mod edit -replace=rsc.io/quote/v3=./local/rsc.io/quote/v3
|
||||
! go get -d rsc.io/quote/v3/missing-package
|
||||
stderr 'module rsc.io/quote/v3@upgrade found \(v3.0.0, replaced by ./local/rsc.io/quote/v3\), but does not contain package'
|
||||
|
||||
-- go.mod --
|
||||
module quoter
|
||||
|
||||
|
||||
11
src/cmd/go/testdata/script/test_timeout.txt
vendored
11
src/cmd/go/testdata/script/test_timeout.txt
vendored
@@ -2,12 +2,13 @@
|
||||
env GO111MODULE=off
|
||||
cd a
|
||||
|
||||
# No timeout is passed via 'go test' command.
|
||||
go test -v
|
||||
# If no timeout is set explicitly, 'go test' should set
|
||||
# -test.timeout to its internal deadline.
|
||||
go test -v . --
|
||||
stdout '10m0s'
|
||||
|
||||
# Timeout is passed via 'go test' command.
|
||||
go test -v -timeout 30m
|
||||
# An explicit -timeout argument should be propagated to -test.timeout.
|
||||
go test -v -timeout 30m . --
|
||||
stdout '30m0s'
|
||||
|
||||
-- a/timeout_test.go --
|
||||
@@ -19,4 +20,4 @@ import (
|
||||
)
|
||||
func TestTimeout(t *testing.T) {
|
||||
fmt.Println(flag.Lookup("test.timeout").Value.String())
|
||||
}
|
||||
}
|
||||
|
||||
16
src/cmd/vendor/golang.org/x/arch/x86/x86asm/decode.go
generated
vendored
16
src/cmd/vendor/golang.org/x/arch/x86/x86asm/decode.go
generated
vendored
@@ -203,7 +203,9 @@ func instPrefix(b byte, mode int) (Inst, error) {
|
||||
// For now we use instPrefix but perhaps later we will return
|
||||
// a specific error here.
|
||||
func truncated(src []byte, mode int) (Inst, error) {
|
||||
// return Inst{}, len(src), ErrTruncated
|
||||
if len(src) == 0 {
|
||||
return Inst{}, ErrTruncated
|
||||
}
|
||||
return instPrefix(src[0], mode) // too long
|
||||
}
|
||||
|
||||
@@ -216,7 +218,6 @@ var (
|
||||
|
||||
// decoderCover records coverage information for which parts
|
||||
// of the byte code have been executed.
|
||||
// TODO(rsc): This is for testing. Only use this if a flag is given.
|
||||
var decoderCover []bool
|
||||
|
||||
// Decode decodes the leading bytes in src as a single instruction.
|
||||
@@ -406,7 +407,7 @@ ReadPrefixes:
|
||||
|
||||
//Group 5 - Vex encoding
|
||||
case 0xC5:
|
||||
if pos == 0 && (mode == 64 || (mode == 32 && pos+1 < len(src) && src[pos+1]&0xc0 == 0xc0)) {
|
||||
if pos == 0 && pos+1 < len(src) && (mode == 64 || (mode == 32 && src[pos+1]&0xc0 == 0xc0)) {
|
||||
vex = p
|
||||
vexIndex = pos
|
||||
inst.Prefix[pos] = p
|
||||
@@ -418,7 +419,7 @@ ReadPrefixes:
|
||||
break ReadPrefixes
|
||||
}
|
||||
case 0xC4:
|
||||
if pos == 0 && (mode == 64 || (mode == 32 && pos+2 < len(src) && src[pos+1]&0xc0 == 0xc0)) {
|
||||
if pos == 0 && pos+2 < len(src) && (mode == 64 || (mode == 32 && src[pos+1]&0xc0 == 0xc0)) {
|
||||
vex = p
|
||||
vexIndex = pos
|
||||
inst.Prefix[pos] = p
|
||||
@@ -460,9 +461,6 @@ ReadPrefixes:
|
||||
// opshift gives the shift to use when saving the next
|
||||
// opcode byte into inst.Opcode.
|
||||
opshift = 24
|
||||
if decoderCover == nil {
|
||||
decoderCover = make([]bool, len(decoder))
|
||||
}
|
||||
|
||||
// Decode loop, executing decoder program.
|
||||
var oldPC, prevPC int
|
||||
@@ -474,7 +472,9 @@ Decode:
|
||||
println("run", pc)
|
||||
}
|
||||
x := decoder[pc]
|
||||
decoderCover[pc] = true
|
||||
if decoderCover != nil {
|
||||
decoderCover[pc] = true
|
||||
}
|
||||
pc++
|
||||
|
||||
// Read and decode ModR/M if needed by opcode.
|
||||
|
||||
2
src/cmd/vendor/modules.txt
vendored
2
src/cmd/vendor/modules.txt
vendored
@@ -16,7 +16,7 @@ github.com/google/pprof/third_party/d3flamegraph
|
||||
github.com/google/pprof/third_party/svgpan
|
||||
# github.com/ianlancetaylor/demangle v0.0.0-20180524225900-fc6590592b44
|
||||
github.com/ianlancetaylor/demangle
|
||||
# golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045
|
||||
# golang.org/x/arch v0.0.0-20190815191158-8a70ba74b3a1
|
||||
golang.org/x/arch/arm/armasm
|
||||
golang.org/x/arch/arm64/arm64asm
|
||||
golang.org/x/arch/ppc64/ppc64asm
|
||||
|
||||
@@ -279,6 +279,9 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
|
||||
}
|
||||
|
||||
w := new(big.Int).ModInverse(s, pub.Q)
|
||||
if w == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
n := pub.Q.BitLen()
|
||||
if n&7 != 0 {
|
||||
|
||||
@@ -189,21 +189,14 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
|
||||
|
||||
// See [NSA] 3.4.1
|
||||
c := priv.PublicKey.Curve
|
||||
e := hashToInt(hash, c)
|
||||
r, s, err = sign(priv, &csprng, c, e)
|
||||
return
|
||||
}
|
||||
|
||||
func signGeneric(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve, e *big.Int) (r, s *big.Int, err error) {
|
||||
N := c.Params().N
|
||||
if N.Sign() == 0 {
|
||||
return nil, nil, errZeroParam
|
||||
}
|
||||
|
||||
var k, kInv *big.Int
|
||||
for {
|
||||
for {
|
||||
k, err = randFieldElement(c, *csprng)
|
||||
k, err = randFieldElement(c, csprng)
|
||||
if err != nil {
|
||||
r = nil
|
||||
return
|
||||
@@ -221,6 +214,8 @@ func signGeneric(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
e := hashToInt(hash, c)
|
||||
s = new(big.Int).Mul(priv.D, r)
|
||||
s.Add(s, e)
|
||||
s.Mul(s, kInv)
|
||||
@@ -229,6 +224,7 @@ func signGeneric(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -246,12 +242,8 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
|
||||
return false
|
||||
}
|
||||
e := hashToInt(hash, c)
|
||||
return verify(pub, c, e, r, s)
|
||||
}
|
||||
|
||||
func verifyGeneric(pub *PublicKey, c elliptic.Curve, e, r, s *big.Int) bool {
|
||||
var w *big.Int
|
||||
N := c.Params().N
|
||||
if in, ok := c.(invertible); ok {
|
||||
w = in.Inverse(s)
|
||||
} else {
|
||||
|
||||
@@ -1,22 +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 !s390x
|
||||
|
||||
package ecdsa
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/elliptic"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func sign(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve, e *big.Int) (r, s *big.Int, err error) {
|
||||
r, s, err = signGeneric(priv, csprng, c, e)
|
||||
return
|
||||
}
|
||||
|
||||
func verify(pub *PublicKey, c elliptic.Curve, e, r, s *big.Int) bool {
|
||||
return verifyGeneric(pub, c, e, r, s)
|
||||
}
|
||||
@@ -1,153 +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 s390x,!gccgo
|
||||
|
||||
package ecdsa
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/elliptic"
|
||||
"internal/cpu"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// s390x accelerated signatures
|
||||
//go:noescape
|
||||
func kdsaSig(fc uint64, block *[1720]byte) (errn uint64)
|
||||
|
||||
type signverify int
|
||||
|
||||
const (
|
||||
signing signverify = iota
|
||||
verifying
|
||||
)
|
||||
|
||||
// bufferOffsets represents the offset of a particular parameter in
|
||||
// the buffer passed to the KDSA instruction.
|
||||
type bufferOffsets struct {
|
||||
baseSize int
|
||||
hashSize int
|
||||
offsetHash int
|
||||
offsetKey1 int
|
||||
offsetRNorKey2 int
|
||||
offsetR int
|
||||
offsetS int
|
||||
functionCode uint64
|
||||
}
|
||||
|
||||
func canUseKDSA(sv signverify, c elliptic.Curve, bo *bufferOffsets) bool {
|
||||
if !cpu.S390X.HasECDSA {
|
||||
return false
|
||||
}
|
||||
|
||||
switch c.Params().Name {
|
||||
case "P-256":
|
||||
bo.baseSize = 32
|
||||
bo.hashSize = 32
|
||||
bo.offsetHash = 64
|
||||
bo.offsetKey1 = 96
|
||||
bo.offsetRNorKey2 = 128
|
||||
bo.offsetR = 0
|
||||
bo.offsetS = 32
|
||||
if sv == signing {
|
||||
bo.functionCode = 137
|
||||
} else {
|
||||
bo.functionCode = 1
|
||||
}
|
||||
return true
|
||||
case "P-384":
|
||||
bo.baseSize = 48
|
||||
bo.hashSize = 48
|
||||
bo.offsetHash = 96
|
||||
bo.offsetKey1 = 144
|
||||
bo.offsetRNorKey2 = 192
|
||||
bo.offsetR = 0
|
||||
bo.offsetS = 48
|
||||
if sv == signing {
|
||||
bo.functionCode = 138
|
||||
} else {
|
||||
bo.functionCode = 2
|
||||
}
|
||||
return true
|
||||
case "P-521":
|
||||
bo.baseSize = 66
|
||||
bo.hashSize = 80
|
||||
bo.offsetHash = 160
|
||||
bo.offsetKey1 = 254
|
||||
bo.offsetRNorKey2 = 334
|
||||
bo.offsetR = 14
|
||||
bo.offsetS = 94
|
||||
if sv == signing {
|
||||
bo.functionCode = 139
|
||||
} else {
|
||||
bo.functionCode = 3
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// zeroExtendAndCopy pads src with leading zeros until it has the size given.
|
||||
// It then copies the padded src into the dst. Bytes beyond size in dst are
|
||||
// not modified.
|
||||
func zeroExtendAndCopy(dst, src []byte, size int) {
|
||||
nz := size - len(src)
|
||||
if nz < 0 {
|
||||
panic("src is too long")
|
||||
}
|
||||
// the compiler should replace this loop with a memclr call
|
||||
z := dst[:nz]
|
||||
for i := range z {
|
||||
z[i] = 0
|
||||
}
|
||||
copy(dst[nz:size], src[:size-nz])
|
||||
return
|
||||
}
|
||||
|
||||
func sign(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve, e *big.Int) (r, s *big.Int, err error) {
|
||||
var bo bufferOffsets
|
||||
if canUseKDSA(signing, c, &bo) && e.Sign() != 0 {
|
||||
var buffer [1720]byte
|
||||
for {
|
||||
var k *big.Int
|
||||
k, err = randFieldElement(c, csprng)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
zeroExtendAndCopy(buffer[bo.offsetHash:], e.Bytes(), bo.hashSize)
|
||||
zeroExtendAndCopy(buffer[bo.offsetKey1:], priv.D.Bytes(), bo.baseSize)
|
||||
zeroExtendAndCopy(buffer[bo.offsetRNorKey2:], k.Bytes(), bo.baseSize)
|
||||
errn := kdsaSig(bo.functionCode, &buffer)
|
||||
if errn == 2 {
|
||||
return nil, nil, errZeroParam
|
||||
}
|
||||
if errn == 0 { // success == 0 means successful signing
|
||||
r = new(big.Int)
|
||||
r.SetBytes(buffer[bo.offsetR : bo.offsetR+bo.baseSize])
|
||||
s = new(big.Int)
|
||||
s.SetBytes(buffer[bo.offsetS : bo.offsetS+bo.baseSize])
|
||||
return
|
||||
}
|
||||
//at this point, it must be that errn == 1: retry
|
||||
}
|
||||
}
|
||||
r, s, err = signGeneric(priv, csprng, c, e)
|
||||
return
|
||||
}
|
||||
|
||||
func verify(pub *PublicKey, c elliptic.Curve, e, r, s *big.Int) bool {
|
||||
var bo bufferOffsets
|
||||
if canUseKDSA(verifying, c, &bo) && e.Sign() != 0 {
|
||||
var buffer [1720]byte
|
||||
zeroExtendAndCopy(buffer[bo.offsetR:], r.Bytes(), bo.baseSize)
|
||||
zeroExtendAndCopy(buffer[bo.offsetS:], s.Bytes(), bo.baseSize)
|
||||
zeroExtendAndCopy(buffer[bo.offsetHash:], e.Bytes(), bo.hashSize)
|
||||
zeroExtendAndCopy(buffer[bo.offsetKey1:], pub.X.Bytes(), bo.baseSize)
|
||||
zeroExtendAndCopy(buffer[bo.offsetRNorKey2:], pub.Y.Bytes(), bo.baseSize)
|
||||
errn := kdsaSig(bo.functionCode, &buffer)
|
||||
return errn == 0
|
||||
}
|
||||
return verifyGeneric(pub, c, e, r, s)
|
||||
}
|
||||
@@ -1,31 +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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func kdsaSig(fc uint64, block *[1720]byte) (errn uint64)
|
||||
TEXT ·kdsaSig(SB), NOSPLIT|NOFRAME, $0-24
|
||||
MOVD fc+0(FP), R0 // function code
|
||||
MOVD block+8(FP), R1 // address parameter block
|
||||
|
||||
loop:
|
||||
WORD $0xB93A0008 // compute digital signature authentication
|
||||
BVS loop // branch back if interrupted
|
||||
BEQ success // signature creation successful
|
||||
BGT retry // signing unsuccessful, but retry with new CSPRN
|
||||
|
||||
error:
|
||||
MOVD $2, R2 // fallthrough indicates fatal error
|
||||
MOVD R2, errn+16(FP) // return 2 - sign/verify abort
|
||||
RET
|
||||
|
||||
retry:
|
||||
MOVD $1, R2
|
||||
MOVD R2, errn+16(FP) // return 1 - sign/verify was unsuccessful -- if sign, retry with new RN
|
||||
RET
|
||||
|
||||
success:
|
||||
MOVD $0, R2
|
||||
MOVD R2, errn+16(FP) // return 0 - sign/verify was successful
|
||||
RET
|
||||
@@ -1,33 +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 s390x,!gccgo
|
||||
|
||||
package ecdsa
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNoAsm(t *testing.T) {
|
||||
curves := [...]elliptic.Curve{
|
||||
elliptic.P256(),
|
||||
elliptic.P384(),
|
||||
elliptic.P521(),
|
||||
}
|
||||
|
||||
for _, curve := range curves {
|
||||
// override the name of the curve to stop the assembly path being taken
|
||||
params := *curve.Params()
|
||||
name := params.Name
|
||||
params.Name = name + "_GENERIC_OVERRIDE"
|
||||
|
||||
testKeyGeneration(t, ¶ms, name)
|
||||
testSignAndVerify(t, ¶ms, name)
|
||||
testNonceSafety(t, ¶ms, name)
|
||||
testINDCCA(t, ¶ms, name)
|
||||
testNegativeInputs(t, ¶ms, name)
|
||||
}
|
||||
}
|
||||
@@ -794,6 +794,10 @@ var supportedVersions = []uint16{
|
||||
func (c *Config) supportedVersions(isClient bool) []uint16 {
|
||||
versions := make([]uint16, 0, len(supportedVersions))
|
||||
for _, v := range supportedVersions {
|
||||
// TLS 1.0 is the default minimum version.
|
||||
if (c == nil || c.MinVersion == 0) && v < VersionTLS10 {
|
||||
continue
|
||||
}
|
||||
if c != nil && c.MinVersion != 0 && v < c.MinVersion {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -77,6 +77,20 @@ func TestRejectBadProtocolVersion(t *testing.T) {
|
||||
}, "unsupported versions")
|
||||
}
|
||||
|
||||
func TestSSLv3OptIn(t *testing.T) {
|
||||
config := testConfig.Clone()
|
||||
config.MinVersion = 0
|
||||
testClientHelloFailure(t, config, &clientHelloMsg{
|
||||
vers: VersionSSL30,
|
||||
random: make([]byte, 32),
|
||||
}, "unsupported versions")
|
||||
testClientHelloFailure(t, config, &clientHelloMsg{
|
||||
vers: VersionTLS12,
|
||||
supportedVersions: []uint16{VersionSSL30},
|
||||
random: make([]byte, 32),
|
||||
}, "unsupported versions")
|
||||
}
|
||||
|
||||
func TestNoSuiteOverlap(t *testing.T) {
|
||||
clientHello := &clientHelloMsg{
|
||||
vers: VersionTLS10,
|
||||
|
||||
@@ -8,9 +8,6 @@ import "bytes"
|
||||
|
||||
// Compact appends to dst the JSON-encoded src with
|
||||
// insignificant space characters elided.
|
||||
// Like Marshal, Compact applies HTMLEscape to any
|
||||
// string literals so that the JSON will be safe to embed
|
||||
// inside HTML <script> tags.
|
||||
func Compact(dst *bytes.Buffer, src []byte) error {
|
||||
return compact(dst, src, false)
|
||||
}
|
||||
|
||||
@@ -13,16 +13,16 @@
|
||||
//
|
||||
// If e.Unwrap() returns a non-nil error w, then we say that e wraps w.
|
||||
//
|
||||
// Unwrap unpacks wrapped errors. If its argument's type has an
|
||||
// Unwrap method, it calls the method once. Otherwise, it returns nil.
|
||||
//
|
||||
// A simple way to create wrapped errors is to call fmt.Errorf and apply the %w verb
|
||||
// to the error argument:
|
||||
//
|
||||
// fmt.Errorf("... %w ...", ..., err, ...).Unwrap()
|
||||
// errors.Unwrap(fmt.Errorf("... %w ...", ..., err, ...))
|
||||
//
|
||||
// returns err.
|
||||
//
|
||||
// Unwrap unpacks wrapped errors. If its argument's type has an
|
||||
// Unwrap method, it calls the method once. Otherwise, it returns nil.
|
||||
//
|
||||
// Is unwraps its first argument sequentially looking for an error that matches the
|
||||
// second. It reports whether it finds a match. It should be used in preference to
|
||||
// simple equality checks:
|
||||
|
||||
@@ -609,7 +609,7 @@ func (s *ss) scanRune(bitSize int) int64 {
|
||||
return r
|
||||
}
|
||||
|
||||
// scanBasePrefix reports whether the integer begins with a bas prefix
|
||||
// scanBasePrefix reports whether the integer begins with a base prefix
|
||||
// and returns the base, digit string, and whether a zero was found.
|
||||
// It is called only if the verb is %v.
|
||||
func (s *ss) scanBasePrefix() (base int, digits string, zeroFound bool) {
|
||||
|
||||
@@ -4,7 +4,7 @@ go 1.12
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8
|
||||
golang.org/x/net v0.0.0-20190607181551-461777fb6f67
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
|
||||
golang.org/x/sys v0.0.0-20190529130038-5219a1e1c5f8 // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
)
|
||||
|
||||
@@ -2,8 +2,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190607181551-461777fb6f67 h1:rJJxsykSlULwd2P2+pg/rtnwN2FrWp4IuCxOSyS0V00=
|
||||
golang.org/x/net v0.0.0-20190607181551-461777fb6f67/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190529130038-5219a1e1c5f8 h1:2WjIC11WRITGlVWmyLXKjzIVj1ZwoWZ//tadeUUV6/o=
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
|
||||
package windows
|
||||
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go security_windows.go psapi_windows.go symlink_windows.go
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall_windows.go security_windows.go psapi_windows.go symlink_windows.go
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
|
||||
package registry
|
||||
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall.go
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall.go
|
||||
|
||||
@@ -134,6 +134,9 @@ func startServer(n, la string, done chan<- string) (addr string, sock io.Closer,
|
||||
}
|
||||
|
||||
func TestWithSimulated(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
|
||||
t.Skipf("sysctl is not supported on iOS")
|
||||
}
|
||||
t.Parallel()
|
||||
msg := "Test 123"
|
||||
var transport []string
|
||||
@@ -272,6 +275,9 @@ func check(t *testing.T, in, out string) {
|
||||
}
|
||||
|
||||
func TestWrite(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
|
||||
t.Skipf("sysctl is not supported on iOS")
|
||||
}
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
pri Priority
|
||||
|
||||
@@ -765,6 +765,14 @@ func (r *Resolver) goLookupPTR(ctx context.Context, addr string) ([]string, erro
|
||||
}
|
||||
}
|
||||
if h.Type != dnsmessage.TypePTR {
|
||||
err := p.SkipAnswer()
|
||||
if err != nil {
|
||||
return nil, &DNSError{
|
||||
Err: "cannot marshal DNS message",
|
||||
Name: addr,
|
||||
Server: server,
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
ptr, err := p.PTRResource()
|
||||
|
||||
@@ -1753,3 +1753,50 @@ func TestDNSUseTCP(t *testing.T) {
|
||||
t.Fatal("exchange failed:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 34660: PTR response with non-PTR answers should ignore non-PTR
|
||||
func TestPTRandNonPTR(t *testing.T) {
|
||||
fake := fakeDNSServer{
|
||||
rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
|
||||
r := dnsmessage.Message{
|
||||
Header: dnsmessage.Header{
|
||||
ID: q.Header.ID,
|
||||
Response: true,
|
||||
RCode: dnsmessage.RCodeSuccess,
|
||||
},
|
||||
Questions: q.Questions,
|
||||
Answers: []dnsmessage.Resource{
|
||||
{
|
||||
Header: dnsmessage.ResourceHeader{
|
||||
Name: q.Questions[0].Name,
|
||||
Type: dnsmessage.TypePTR,
|
||||
Class: dnsmessage.ClassINET,
|
||||
},
|
||||
Body: &dnsmessage.PTRResource{
|
||||
PTR: dnsmessage.MustNewName("golang.org."),
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: dnsmessage.ResourceHeader{
|
||||
Name: q.Questions[0].Name,
|
||||
Type: dnsmessage.TypeTXT,
|
||||
Class: dnsmessage.ClassINET,
|
||||
},
|
||||
Body: &dnsmessage.TXTResource{
|
||||
TXT: []string{"PTR 8 6 60 ..."}, // fake RRSIG
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return r, nil
|
||||
},
|
||||
}
|
||||
r := Resolver{PreferGo: true, Dial: fake.DialContext}
|
||||
names, err := r.lookupAddr(context.Background(), "192.0.2.123")
|
||||
if err != nil {
|
||||
t.Fatalf("LookupAddr: %v", err)
|
||||
}
|
||||
if want := []string{"golang.org."}; !reflect.DeepEqual(names, want) {
|
||||
t.Errorf("names = %q; want %q", names, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ func send(ireq *Request, rt RoundTripper, deadline time.Time) (resp *Response, d
|
||||
username := u.Username()
|
||||
password, _ := u.Password()
|
||||
forkReq()
|
||||
req.Header = ireq.Header.Clone()
|
||||
req.Header = cloneOrMakeHeader(ireq.Header)
|
||||
req.Header.Set("Authorization", "Basic "+basicAuth(username, password))
|
||||
}
|
||||
|
||||
@@ -668,7 +668,7 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) {
|
||||
// The headers to copy are from the very initial request.
|
||||
// We use a closured callback to keep a reference to these original headers.
|
||||
var (
|
||||
ireqhdr = ireq.Header.Clone()
|
||||
ireqhdr = cloneOrMakeHeader(ireq.Header)
|
||||
icookies map[string][]*Cookie
|
||||
)
|
||||
if c.Jar != nil && ireq.Header.Get("Cookie") != "" {
|
||||
|
||||
@@ -62,3 +62,13 @@ func cloneMultipartFileHeader(fh *multipart.FileHeader) *multipart.FileHeader {
|
||||
fh2.Header = textproto.MIMEHeader(Header(fh.Header).Clone())
|
||||
return fh2
|
||||
}
|
||||
|
||||
// cloneOrMakeHeader invokes Header.Clone but if the
|
||||
// result is nil, it'll instead make and return a non-nil Header.
|
||||
func cloneOrMakeHeader(hdr Header) Header {
|
||||
clone := hdr.Clone()
|
||||
if clone == nil {
|
||||
clone = make(Header)
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
@@ -208,6 +208,30 @@ func (t *Transport) PutIdleTestConn(scheme, addr string) bool {
|
||||
}) == nil
|
||||
}
|
||||
|
||||
// PutIdleTestConnH2 reports whether it was able to insert a fresh
|
||||
// HTTP/2 persistConn for scheme, addr into the idle connection pool.
|
||||
func (t *Transport) PutIdleTestConnH2(scheme, addr string, alt RoundTripper) bool {
|
||||
key := connectMethodKey{"", scheme, addr, false}
|
||||
|
||||
if t.MaxConnsPerHost > 0 {
|
||||
// Transport is tracking conns-per-host.
|
||||
// Increment connection count to account
|
||||
// for new persistConn created below.
|
||||
t.connsPerHostMu.Lock()
|
||||
if t.connsPerHost == nil {
|
||||
t.connsPerHost = make(map[connectMethodKey]int)
|
||||
}
|
||||
t.connsPerHost[key]++
|
||||
t.connsPerHostMu.Unlock()
|
||||
}
|
||||
|
||||
return t.tryPutIdleConn(&persistConn{
|
||||
t: t,
|
||||
alt: alt,
|
||||
cacheKey: key,
|
||||
}) == nil
|
||||
}
|
||||
|
||||
// All test hooks must be non-nil so they can be called directly,
|
||||
// but the tests use nil to mean hook disabled.
|
||||
func unnilTestHook(f *func()) {
|
||||
|
||||
@@ -3611,10 +3611,11 @@ func (p *http2pipe) Done() <-chan struct{} {
|
||||
}
|
||||
|
||||
const (
|
||||
http2prefaceTimeout = 10 * time.Second
|
||||
http2firstSettingsTimeout = 2 * time.Second // should be in-flight with preface anyway
|
||||
http2handlerChunkWriteSize = 4 << 10
|
||||
http2defaultMaxStreams = 250 // TODO: make this 100 as the GFE seems to?
|
||||
http2prefaceTimeout = 10 * time.Second
|
||||
http2firstSettingsTimeout = 2 * time.Second // should be in-flight with preface anyway
|
||||
http2handlerChunkWriteSize = 4 << 10
|
||||
http2defaultMaxStreams = 250 // TODO: make this 100 as the GFE seems to?
|
||||
http2maxQueuedControlFrames = 10000
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -3722,6 +3723,15 @@ func (s *http2Server) maxConcurrentStreams() uint32 {
|
||||
return http2defaultMaxStreams
|
||||
}
|
||||
|
||||
// maxQueuedControlFrames is the maximum number of control frames like
|
||||
// SETTINGS, PING and RST_STREAM that will be queued for writing before
|
||||
// the connection is closed to prevent memory exhaustion attacks.
|
||||
func (s *http2Server) maxQueuedControlFrames() int {
|
||||
// TODO: if anybody asks, add a Server field, and remember to define the
|
||||
// behavior of negative values.
|
||||
return http2maxQueuedControlFrames
|
||||
}
|
||||
|
||||
type http2serverInternalState struct {
|
||||
mu sync.Mutex
|
||||
activeConns map[*http2serverConn]struct{}
|
||||
@@ -4065,6 +4075,7 @@ type http2serverConn struct {
|
||||
sawFirstSettings bool // got the initial SETTINGS frame after the preface
|
||||
needToSendSettingsAck bool
|
||||
unackedSettings int // how many SETTINGS have we sent without ACKs?
|
||||
queuedControlFrames int // control frames in the writeSched queue
|
||||
clientMaxStreams uint32 // SETTINGS_MAX_CONCURRENT_STREAMS from client (our PUSH_PROMISE limit)
|
||||
advMaxStreams uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client
|
||||
curClientStreams uint32 // number of open streams initiated by the client
|
||||
@@ -4456,6 +4467,14 @@ func (sc *http2serverConn) serve() {
|
||||
}
|
||||
}
|
||||
|
||||
// If the peer is causing us to generate a lot of control frames,
|
||||
// but not reading them from us, assume they are trying to make us
|
||||
// run out of memory.
|
||||
if sc.queuedControlFrames > sc.srv.maxQueuedControlFrames() {
|
||||
sc.vlogf("http2: too many control frames in send queue, closing connection")
|
||||
return
|
||||
}
|
||||
|
||||
// Start the shutdown timer after sending a GOAWAY. When sending GOAWAY
|
||||
// with no error code (graceful shutdown), don't start the timer until
|
||||
// all open streams have been completed.
|
||||
@@ -4657,6 +4676,14 @@ func (sc *http2serverConn) writeFrame(wr http2FrameWriteRequest) {
|
||||
}
|
||||
|
||||
if !ignoreWrite {
|
||||
if wr.isControl() {
|
||||
sc.queuedControlFrames++
|
||||
// For extra safety, detect wraparounds, which should not happen,
|
||||
// and pull the plug.
|
||||
if sc.queuedControlFrames < 0 {
|
||||
sc.conn.Close()
|
||||
}
|
||||
}
|
||||
sc.writeSched.Push(wr)
|
||||
}
|
||||
sc.scheduleFrameWrite()
|
||||
@@ -4774,10 +4801,8 @@ func (sc *http2serverConn) wroteFrame(res http2frameWriteResult) {
|
||||
// If a frame is already being written, nothing happens. This will be called again
|
||||
// when the frame is done being written.
|
||||
//
|
||||
// If a frame isn't being written we need to send one, the best frame
|
||||
// to send is selected, preferring first things that aren't
|
||||
// stream-specific (e.g. ACKing settings), and then finding the
|
||||
// highest priority stream.
|
||||
// If a frame isn't being written and we need to send one, the best frame
|
||||
// to send is selected by writeSched.
|
||||
//
|
||||
// If a frame isn't being written and there's nothing else to send, we
|
||||
// flush the write buffer.
|
||||
@@ -4805,6 +4830,9 @@ func (sc *http2serverConn) scheduleFrameWrite() {
|
||||
}
|
||||
if !sc.inGoAway || sc.goAwayCode == http2ErrCodeNo {
|
||||
if wr, ok := sc.writeSched.Pop(); ok {
|
||||
if wr.isControl() {
|
||||
sc.queuedControlFrames--
|
||||
}
|
||||
sc.startFrameWrite(wr)
|
||||
continue
|
||||
}
|
||||
@@ -5097,6 +5125,8 @@ func (sc *http2serverConn) processSettings(f *http2SettingsFrame) error {
|
||||
if err := f.ForeachSetting(sc.processSetting); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: judging by RFC 7540, Section 6.5.3 each SETTINGS frame should be
|
||||
// acknowledged individually, even if multiple are received before the ACK.
|
||||
sc.needToSendSettingsAck = true
|
||||
sc.scheduleFrameWrite()
|
||||
return nil
|
||||
@@ -7451,7 +7481,7 @@ func (cc *http2ClientConn) roundTrip(req *Request) (res *Response, gotErrAfterRe
|
||||
req.Method != "HEAD" {
|
||||
// Request gzip only, not deflate. Deflate is ambiguous and
|
||||
// not as universally supported anyway.
|
||||
// See: http://www.gzip.org/zlib/zlib_faq.html#faq38
|
||||
// See: https://zlib.net/zlib_faq.html#faq39
|
||||
//
|
||||
// Note that we don't request this for HEAD requests,
|
||||
// due to a bug in nginx:
|
||||
@@ -9445,7 +9475,7 @@ type http2WriteScheduler interface {
|
||||
|
||||
// Pop dequeues the next frame to write. Returns false if no frames can
|
||||
// be written. Frames with a given wr.StreamID() are Pop'd in the same
|
||||
// order they are Push'd.
|
||||
// order they are Push'd. No frames should be discarded except by CloseStream.
|
||||
Pop() (wr http2FrameWriteRequest, ok bool)
|
||||
}
|
||||
|
||||
@@ -9489,6 +9519,12 @@ func (wr http2FrameWriteRequest) StreamID() uint32 {
|
||||
return wr.stream.id
|
||||
}
|
||||
|
||||
// isControl reports whether wr is a control frame for MaxQueuedControlFrames
|
||||
// purposes. That includes non-stream frames and RST_STREAM frames.
|
||||
func (wr http2FrameWriteRequest) isControl() bool {
|
||||
return wr.stream == nil
|
||||
}
|
||||
|
||||
// DataSize returns the number of flow control bytes that must be consumed
|
||||
// to write this entire frame. This is 0 for non-DATA frames.
|
||||
func (wr http2FrameWriteRequest) DataSize() int {
|
||||
@@ -10112,7 +10148,8 @@ type http2randomWriteScheduler struct {
|
||||
zero http2writeQueue
|
||||
|
||||
// sq contains the stream-specific queues, keyed by stream ID.
|
||||
// When a stream is idle or closed, it's deleted from the map.
|
||||
// When a stream is idle, closed, or emptied, it's deleted
|
||||
// from the map.
|
||||
sq map[uint32]*http2writeQueue
|
||||
|
||||
// pool of empty queues for reuse.
|
||||
@@ -10156,8 +10193,12 @@ func (ws *http2randomWriteScheduler) Pop() (http2FrameWriteRequest, bool) {
|
||||
return ws.zero.shift(), true
|
||||
}
|
||||
// Iterate over all non-idle streams until finding one that can be consumed.
|
||||
for _, q := range ws.sq {
|
||||
for streamID, q := range ws.sq {
|
||||
if wr, ok := q.consume(math.MaxInt32); ok {
|
||||
if q.empty() {
|
||||
delete(ws.sq, streamID)
|
||||
ws.queuePool.put(q)
|
||||
}
|
||||
return wr, true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package http
|
||||
import (
|
||||
"bytes"
|
||||
"internal/race"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -219,3 +220,34 @@ func TestHeaderWriteSubsetAllocs(t *testing.T) {
|
||||
t.Errorf("allocs = %g; want 0", n)
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 34878: test that every call to
|
||||
// cloneOrMakeHeader never returns a nil Header.
|
||||
func TestCloneOrMakeHeader(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in, want Header
|
||||
}{
|
||||
{"nil", nil, Header{}},
|
||||
{"empty", Header{}, Header{}},
|
||||
{
|
||||
name: "non-empty",
|
||||
in: Header{"foo": {"bar"}},
|
||||
want: Header{"foo": {"bar"}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := cloneOrMakeHeader(tt.in)
|
||||
if got == nil {
|
||||
t.Fatal("unexpected nil Header")
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Fatalf("Got: %#v\nWant: %#v", got, tt.want)
|
||||
}
|
||||
got.Add("A", "B")
|
||||
got.Get("A")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -826,6 +826,34 @@ func TestWithContextDeepCopiesURL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoPanicOnRoundTripWithBasicAuth_h1(t *testing.T) {
|
||||
testNoPanicWithBasicAuth(t, h1Mode)
|
||||
}
|
||||
|
||||
func TestNoPanicOnRoundTripWithBasicAuth_h2(t *testing.T) {
|
||||
testNoPanicWithBasicAuth(t, h2Mode)
|
||||
}
|
||||
|
||||
// Issue 34878: verify we don't panic when including basic auth (Go 1.13 regression)
|
||||
func testNoPanicWithBasicAuth(t *testing.T, h2 bool) {
|
||||
defer afterTest(t)
|
||||
cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {}))
|
||||
defer cst.close()
|
||||
|
||||
u, err := url.Parse(cst.ts.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
u.User = url.UserPassword("foo", "bar")
|
||||
req := &Request{
|
||||
URL: u,
|
||||
Method: "GET",
|
||||
}
|
||||
if _, err := cst.c.Do(req); err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// verify that NewRequest sets Request.GetBody and that it works
|
||||
func TestNewRequestGetBody(t *testing.T) {
|
||||
tests := []struct {
|
||||
|
||||
@@ -4755,6 +4755,10 @@ func TestServerValidatesHeaders(t *testing.T) {
|
||||
{"foo\xffbar: foo\r\n", 400}, // binary in header
|
||||
{"foo\x00bar: foo\r\n", 400}, // binary in header
|
||||
{"Foo: " + strings.Repeat("x", 1<<21) + "\r\n", 431}, // header too large
|
||||
// Spaces between the header key and colon are not allowed.
|
||||
// See RFC 7230, Section 3.2.4.
|
||||
{"Foo : bar\r\n", 400},
|
||||
{"Foo\t: bar\r\n", 400},
|
||||
|
||||
{"foo: foo foo\r\n", 200}, // LWS space is okay
|
||||
{"foo: foo\tfoo\r\n", 200}, // LWS tab is okay
|
||||
|
||||
@@ -231,13 +231,13 @@ type CloseNotifier interface {
|
||||
|
||||
var (
|
||||
// ServerContextKey is a context key. It can be used in HTTP
|
||||
// handlers with context.WithValue to access the server that
|
||||
// handlers with Context.Value to access the server that
|
||||
// started the handler. The associated value will be of
|
||||
// type *Server.
|
||||
ServerContextKey = &contextKey{"http-server"}
|
||||
|
||||
// LocalAddrContextKey is a context key. It can be used in
|
||||
// HTTP handlers with context.WithValue to access the local
|
||||
// HTTP handlers with Context.Value to access the local
|
||||
// address the connection arrived on.
|
||||
// The associated value will be of type net.Addr.
|
||||
LocalAddrContextKey = &contextKey{"local-addr"}
|
||||
@@ -3177,8 +3177,8 @@ func (srv *Server) onceSetNextProtoDefaults() {
|
||||
// After such a timeout, writes by h to its ResponseWriter will return
|
||||
// ErrHandlerTimeout.
|
||||
//
|
||||
// TimeoutHandler buffers all Handler writes to memory and does not
|
||||
// support the Hijacker or Flusher interfaces.
|
||||
// TimeoutHandler supports the Flusher and Pusher interfaces but does not
|
||||
// support the Hijacker interface.
|
||||
func TimeoutHandler(h Handler, dt time.Duration, msg string) Handler {
|
||||
return &timeoutHandler{
|
||||
handler: h,
|
||||
@@ -3267,7 +3267,6 @@ type timeoutWriter struct {
|
||||
}
|
||||
|
||||
var _ Pusher = (*timeoutWriter)(nil)
|
||||
var _ Flusher = (*timeoutWriter)(nil)
|
||||
|
||||
// Push implements the Pusher interface.
|
||||
func (tw *timeoutWriter) Push(target string, opts *PushOptions) error {
|
||||
@@ -3277,14 +3276,6 @@ func (tw *timeoutWriter) Push(target string, opts *PushOptions) error {
|
||||
return ErrNotSupported
|
||||
}
|
||||
|
||||
// Flush implements the Flusher interface.
|
||||
func (tw *timeoutWriter) Flush() {
|
||||
f, ok := tw.w.(Flusher)
|
||||
if ok {
|
||||
f.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func (tw *timeoutWriter) Header() Header { return tw.h }
|
||||
|
||||
func (tw *timeoutWriter) Write(p []byte) (int, error) {
|
||||
|
||||
@@ -539,6 +539,7 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) {
|
||||
}
|
||||
if http2isNoCachedConnError(err) {
|
||||
t.removeIdleConn(pconn)
|
||||
t.decConnsPerHost(pconn.cacheKey)
|
||||
} else if !pconn.shouldRetryRequest(req, err) {
|
||||
// Issue 16465: return underlying net.Conn.Read error from peek,
|
||||
// as we've historically done.
|
||||
@@ -953,6 +954,7 @@ func (t *Transport) queueForIdleConn(w *wantConn) (delivered bool) {
|
||||
t.idleConnWait = make(map[connectMethodKey]wantConnQueue)
|
||||
}
|
||||
q := t.idleConnWait[w.key]
|
||||
q.cleanFront()
|
||||
q.pushBack(w)
|
||||
t.idleConnWait[w.key] = q
|
||||
return false
|
||||
@@ -1137,7 +1139,7 @@ func (q *wantConnQueue) pushBack(w *wantConn) {
|
||||
q.tail = append(q.tail, w)
|
||||
}
|
||||
|
||||
// popFront removes and returns the w at the front of the queue.
|
||||
// popFront removes and returns the wantConn at the front of the queue.
|
||||
func (q *wantConnQueue) popFront() *wantConn {
|
||||
if q.headPos >= len(q.head) {
|
||||
if len(q.tail) == 0 {
|
||||
@@ -1152,6 +1154,30 @@ func (q *wantConnQueue) popFront() *wantConn {
|
||||
return w
|
||||
}
|
||||
|
||||
// peekFront returns the wantConn at the front of the queue without removing it.
|
||||
func (q *wantConnQueue) peekFront() *wantConn {
|
||||
if q.headPos < len(q.head) {
|
||||
return q.head[q.headPos]
|
||||
}
|
||||
if len(q.tail) > 0 {
|
||||
return q.tail[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanFront pops any wantConns that are no longer waiting from the head of the
|
||||
// queue, reporting whether any were popped.
|
||||
func (q *wantConnQueue) cleanFront() (cleaned bool) {
|
||||
for {
|
||||
w := q.peekFront()
|
||||
if w == nil || w.waiting() {
|
||||
return cleaned
|
||||
}
|
||||
q.popFront()
|
||||
cleaned = true
|
||||
}
|
||||
}
|
||||
|
||||
// getConn dials and creates a new persistConn to the target as
|
||||
// specified in the connectMethod. This includes doing a proxy CONNECT
|
||||
// and/or setting up TLS. If this doesn't return an error, the persistConn
|
||||
@@ -1181,7 +1207,9 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (pc *persi
|
||||
// Queue for idle connection.
|
||||
if delivered := t.queueForIdleConn(w); delivered {
|
||||
pc := w.pc
|
||||
if trace != nil && trace.GotConn != nil {
|
||||
// Trace only for HTTP/1.
|
||||
// HTTP/2 calls trace.GotConn itself.
|
||||
if pc.alt == nil && trace != nil && trace.GotConn != nil {
|
||||
trace.GotConn(pc.gotIdleConnTrace(pc.idleAt))
|
||||
}
|
||||
// set request canceler to some non-nil function so we
|
||||
@@ -1261,6 +1289,7 @@ func (t *Transport) queueForDial(w *wantConn) {
|
||||
t.connsPerHostWait = make(map[connectMethodKey]wantConnQueue)
|
||||
}
|
||||
q := t.connsPerHostWait[w.key]
|
||||
q.cleanFront()
|
||||
q.pushBack(w)
|
||||
t.connsPerHostWait[w.key] = q
|
||||
}
|
||||
|
||||
@@ -1658,6 +1658,176 @@ func TestTransportPersistConnLeakShortBody(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// A countedConn is a net.Conn that decrements an atomic counter when finalized.
|
||||
type countedConn struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
// A countingDialer dials connections and counts the number that remain reachable.
|
||||
type countingDialer struct {
|
||||
dialer net.Dialer
|
||||
mu sync.Mutex
|
||||
total, live int64
|
||||
}
|
||||
|
||||
func (d *countingDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
conn, err := d.dialer.DialContext(ctx, network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
counted := new(countedConn)
|
||||
counted.Conn = conn
|
||||
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
d.total++
|
||||
d.live++
|
||||
|
||||
runtime.SetFinalizer(counted, d.decrement)
|
||||
return counted, nil
|
||||
}
|
||||
|
||||
func (d *countingDialer) decrement(*countedConn) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
d.live--
|
||||
}
|
||||
|
||||
func (d *countingDialer) Read() (total, live int64) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
return d.total, d.live
|
||||
}
|
||||
|
||||
func TestTransportPersistConnLeakNeverIdle(t *testing.T) {
|
||||
defer afterTest(t)
|
||||
|
||||
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
||||
// Close every connection so that it cannot be kept alive.
|
||||
conn, _, err := w.(Hijacker).Hijack()
|
||||
if err != nil {
|
||||
t.Errorf("Hijack failed unexpectedly: %v", err)
|
||||
return
|
||||
}
|
||||
conn.Close()
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
var d countingDialer
|
||||
c := ts.Client()
|
||||
c.Transport.(*Transport).DialContext = d.DialContext
|
||||
|
||||
body := []byte("Hello")
|
||||
for i := 0; ; i++ {
|
||||
total, live := d.Read()
|
||||
if live < total {
|
||||
break
|
||||
}
|
||||
if i >= 1<<12 {
|
||||
t.Fatalf("Count of live client net.Conns (%d) not lower than total (%d) after %d Do / GC iterations.", live, total, i)
|
||||
}
|
||||
|
||||
req, err := NewRequest("POST", ts.URL, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = c.Do(req)
|
||||
if err == nil {
|
||||
t.Fatal("expected broken connection")
|
||||
}
|
||||
|
||||
runtime.GC()
|
||||
}
|
||||
}
|
||||
|
||||
type countedContext struct {
|
||||
context.Context
|
||||
}
|
||||
|
||||
type contextCounter struct {
|
||||
mu sync.Mutex
|
||||
live int64
|
||||
}
|
||||
|
||||
func (cc *contextCounter) Track(ctx context.Context) context.Context {
|
||||
counted := new(countedContext)
|
||||
counted.Context = ctx
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
cc.live++
|
||||
runtime.SetFinalizer(counted, cc.decrement)
|
||||
return counted
|
||||
}
|
||||
|
||||
func (cc *contextCounter) decrement(*countedContext) {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
cc.live--
|
||||
}
|
||||
|
||||
func (cc *contextCounter) Read() (live int64) {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
return cc.live
|
||||
}
|
||||
|
||||
func TestTransportPersistConnContextLeakMaxConnsPerHost(t *testing.T) {
|
||||
defer afterTest(t)
|
||||
|
||||
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
||||
runtime.Gosched()
|
||||
w.WriteHeader(StatusOK)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
c := ts.Client()
|
||||
c.Transport.(*Transport).MaxConnsPerHost = 1
|
||||
|
||||
ctx := context.Background()
|
||||
body := []byte("Hello")
|
||||
doPosts := func(cc *contextCounter) {
|
||||
var wg sync.WaitGroup
|
||||
for n := 64; n > 0; n-- {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
ctx := cc.Track(ctx)
|
||||
req, err := NewRequest("POST", ts.URL, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = c.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
t.Errorf("Do failed with error: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
var initialCC contextCounter
|
||||
doPosts(&initialCC)
|
||||
|
||||
// flushCC exists only to put pressure on the GC to finalize the initialCC
|
||||
// contexts: the flushCC allocations should eventually displace the initialCC
|
||||
// allocations.
|
||||
var flushCC contextCounter
|
||||
for i := 0; ; i++ {
|
||||
live := initialCC.Read()
|
||||
if live == 0 {
|
||||
break
|
||||
}
|
||||
if i >= 100 {
|
||||
t.Fatalf("%d Contexts still not finalized after %d GC cycles.", live, i)
|
||||
}
|
||||
doPosts(&flushCC)
|
||||
runtime.GC()
|
||||
}
|
||||
}
|
||||
|
||||
// This used to crash; https://golang.org/issue/3266
|
||||
func TestTransportIdleConnCrash(t *testing.T) {
|
||||
defer afterTest(t)
|
||||
@@ -3392,6 +3562,76 @@ func TestTransportCloseIdleConnsThenReturn(t *testing.T) {
|
||||
wantIdle("after final put", 1)
|
||||
}
|
||||
|
||||
// Test for issue 34282
|
||||
// Ensure that getConn doesn't call the GotConn trace hook on a HTTP/2 idle conn
|
||||
func TestTransportTraceGotConnH2IdleConns(t *testing.T) {
|
||||
tr := &Transport{}
|
||||
wantIdle := func(when string, n int) bool {
|
||||
got := tr.IdleConnCountForTesting("https", "example.com:443") // key used by PutIdleTestConnH2
|
||||
if got == n {
|
||||
return true
|
||||
}
|
||||
t.Errorf("%s: idle conns = %d; want %d", when, got, n)
|
||||
return false
|
||||
}
|
||||
wantIdle("start", 0)
|
||||
alt := funcRoundTripper(func() {})
|
||||
if !tr.PutIdleTestConnH2("https", "example.com:443", alt) {
|
||||
t.Fatal("put failed")
|
||||
}
|
||||
wantIdle("after put", 1)
|
||||
ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{
|
||||
GotConn: func(httptrace.GotConnInfo) {
|
||||
// tr.getConn should leave it for the HTTP/2 alt to call GotConn.
|
||||
t.Error("GotConn called")
|
||||
},
|
||||
})
|
||||
req, _ := NewRequestWithContext(ctx, MethodGet, "https://example.com", nil)
|
||||
_, err := tr.RoundTrip(req)
|
||||
if err != errFakeRoundTrip {
|
||||
t.Errorf("got error: %v; want %q", err, errFakeRoundTrip)
|
||||
}
|
||||
wantIdle("after round trip", 1)
|
||||
}
|
||||
|
||||
func TestTransportRemovesH2ConnsAfterIdle(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping in short mode")
|
||||
}
|
||||
|
||||
trFunc := func(tr *Transport) {
|
||||
tr.MaxConnsPerHost = 1
|
||||
tr.MaxIdleConnsPerHost = 1
|
||||
tr.IdleConnTimeout = 10 * time.Millisecond
|
||||
}
|
||||
cst := newClientServerTest(t, h2Mode, HandlerFunc(func(w ResponseWriter, r *Request) {}), trFunc)
|
||||
defer cst.close()
|
||||
|
||||
if _, err := cst.c.Get(cst.ts.URL); err != nil {
|
||||
t.Fatalf("got error: %s", err)
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
got := make(chan error)
|
||||
go func() {
|
||||
if _, err := cst.c.Get(cst.ts.URL); err != nil {
|
||||
got <- err
|
||||
}
|
||||
close(got)
|
||||
}()
|
||||
|
||||
timeout := time.NewTimer(5 * time.Second)
|
||||
defer timeout.Stop()
|
||||
select {
|
||||
case err := <-got:
|
||||
if err != nil {
|
||||
t.Fatalf("got error: %s", err)
|
||||
}
|
||||
case <-timeout.C:
|
||||
t.Fatal("request never completed")
|
||||
}
|
||||
}
|
||||
|
||||
// This tests that an client requesting a content range won't also
|
||||
// implicitly ask for gzip support. If they want that, they need to do it
|
||||
// on their own.
|
||||
@@ -5452,3 +5692,30 @@ func TestTransportIgnores408(t *testing.T) {
|
||||
}
|
||||
t.Fatalf("timeout after %v waiting for Transport connections to die off", time.Since(t0))
|
||||
}
|
||||
|
||||
func TestInvalidHeaderResponse(t *testing.T) {
|
||||
setParallel(t)
|
||||
defer afterTest(t)
|
||||
cst := newClientServerTest(t, h1Mode, HandlerFunc(func(w ResponseWriter, r *Request) {
|
||||
conn, buf, _ := w.(Hijacker).Hijack()
|
||||
buf.Write([]byte("HTTP/1.1 200 OK\r\n" +
|
||||
"Date: Wed, 30 Aug 2017 19:09:27 GMT\r\n" +
|
||||
"Content-Type: text/html; charset=utf-8\r\n" +
|
||||
"Content-Length: 0\r\n" +
|
||||
"Foo : bar\r\n\r\n"))
|
||||
buf.Flush()
|
||||
conn.Close()
|
||||
}))
|
||||
defer cst.close()
|
||||
res, err := cst.c.Get(cst.ts.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if v := res.Header.Get("Foo"); v != "" {
|
||||
t.Errorf(`unexpected "Foo" header: %q`, v)
|
||||
}
|
||||
if v := res.Header.Get("Foo "); v != "bar" {
|
||||
t.Errorf(`bad "Foo " header value: %q, want %q`, v, "bar")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,9 @@ func ipv6LinkLocalUnicastAddr(ifi *Interface) string {
|
||||
}
|
||||
|
||||
func TestInterfaces(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
|
||||
t.Skipf("sysctl is not supported on iOS")
|
||||
}
|
||||
ift, err := Interfaces()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -82,6 +85,9 @@ func TestInterfaces(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInterfaceAddrs(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
|
||||
t.Skipf("sysctl is not supported on iOS")
|
||||
}
|
||||
ift, err := Interfaces()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -101,6 +107,9 @@ func TestInterfaceAddrs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInterfaceUnicastAddrs(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
|
||||
t.Skipf("sysctl is not supported on iOS")
|
||||
}
|
||||
ift, err := Interfaces()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -128,6 +137,9 @@ func TestInterfaceUnicastAddrs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInterfaceMulticastAddrs(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
|
||||
t.Skipf("sysctl is not supported on iOS")
|
||||
}
|
||||
ift, err := Interfaces()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -495,18 +495,12 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
|
||||
return m, err
|
||||
}
|
||||
|
||||
// Key ends at first colon; should not have trailing spaces
|
||||
// but they appear in the wild, violating specs, so we remove
|
||||
// them if present.
|
||||
// Key ends at first colon.
|
||||
i := bytes.IndexByte(kv, ':')
|
||||
if i < 0 {
|
||||
return m, ProtocolError("malformed MIME header line: " + string(kv))
|
||||
}
|
||||
endKey := i
|
||||
for endKey > 0 && kv[endKey-1] == ' ' {
|
||||
endKey--
|
||||
}
|
||||
key := canonicalMIMEHeaderKey(kv[:endKey])
|
||||
key := canonicalMIMEHeaderKey(kv[:i])
|
||||
|
||||
// As per RFC 7230 field-name is a token, tokens consist of one or more chars.
|
||||
// We could return a ProtocolError here, but better to be liberal in what we
|
||||
|
||||
@@ -188,11 +188,10 @@ func TestLargeReadMIMEHeader(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test that we read slightly-bogus MIME headers seen in the wild,
|
||||
// with spaces before colons, and spaces in keys.
|
||||
// TestReadMIMEHeaderNonCompliant checks that we don't normalize headers
|
||||
// with spaces before colons, and accept spaces in keys.
|
||||
func TestReadMIMEHeaderNonCompliant(t *testing.T) {
|
||||
// Invalid HTTP response header as sent by an Axis security
|
||||
// camera: (this is handled by IE, Firefox, Chrome, curl, etc.)
|
||||
// These invalid headers will be rejected by net/http according to RFC 7230.
|
||||
r := reader("Foo: bar\r\n" +
|
||||
"Content-Language: en\r\n" +
|
||||
"SID : 0\r\n" +
|
||||
@@ -202,9 +201,9 @@ func TestReadMIMEHeaderNonCompliant(t *testing.T) {
|
||||
want := MIMEHeader{
|
||||
"Foo": {"bar"},
|
||||
"Content-Language": {"en"},
|
||||
"Sid": {"0"},
|
||||
"Audio Mode": {"None"},
|
||||
"Privilege": {"127"},
|
||||
"SID ": {"0"},
|
||||
"Audio Mode ": {"None"},
|
||||
"Privilege ": {"127"},
|
||||
}
|
||||
if !reflect.DeepEqual(m, want) || err != nil {
|
||||
t.Fatalf("ReadMIMEHeader =\n%v, %v; want:\n%v", m, err, want)
|
||||
|
||||
@@ -1521,6 +1521,9 @@ func testWindowsHostname(t *testing.T, hostname string) {
|
||||
}
|
||||
|
||||
func TestHostname(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
|
||||
t.Skipf("sysctl is not supported on iOS")
|
||||
}
|
||||
hostname, err := Hostname()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -345,6 +345,9 @@ func ReadMemStatsSlow() (base, slow MemStats) {
|
||||
slow.HeapReleased += uint64(i.span().released())
|
||||
}
|
||||
|
||||
// Unused space in the current arena also counts as released space.
|
||||
slow.HeapReleased += uint64(mheap_.curArena.end - mheap_.curArena.base)
|
||||
|
||||
getg().m.mallocing--
|
||||
})
|
||||
|
||||
|
||||
@@ -229,6 +229,8 @@ func setGCPercent(in int32) (out int32) {
|
||||
gcSetTriggerRatio(memstats.triggerRatio)
|
||||
unlock(&mheap_.lock)
|
||||
})
|
||||
// Pacing changed, so the scavenger should be awoken.
|
||||
wakeScavenger()
|
||||
|
||||
// If we just disabled GC, wait for any concurrent GC mark to
|
||||
// finish so we always return with no GC running.
|
||||
@@ -1649,9 +1651,16 @@ func gcMarkTermination(nextTriggerRatio float64) {
|
||||
throw("gc done but gcphase != _GCoff")
|
||||
}
|
||||
|
||||
// Record next_gc and heap_inuse for scavenger.
|
||||
memstats.last_next_gc = memstats.next_gc
|
||||
memstats.last_heap_inuse = memstats.heap_inuse
|
||||
|
||||
// Update GC trigger and pacing for the next cycle.
|
||||
gcSetTriggerRatio(nextTriggerRatio)
|
||||
|
||||
// Pacing changed, so the scavenger should be awoken.
|
||||
wakeScavenger()
|
||||
|
||||
// Update timing memstats
|
||||
now := nanotime()
|
||||
sec, nsec, _ := time_now()
|
||||
|
||||
@@ -17,7 +17,29 @@
|
||||
// scavenger's primary goal is to bring the estimated heap RSS of the
|
||||
// application down to a goal.
|
||||
//
|
||||
// That goal is defined as (retainExtraPercent+100) / 100 * next_gc.
|
||||
// That goal is defined as:
|
||||
// (retainExtraPercent+100) / 100 * (next_gc / last_next_gc) * last_heap_inuse
|
||||
//
|
||||
// Essentially, we wish to have the application's RSS track the heap goal, but
|
||||
// the heap goal is defined in terms of bytes of objects, rather than pages like
|
||||
// RSS. As a result, we need to take into account for fragmentation internal to
|
||||
// spans. next_gc / last_next_gc defines the ratio between the current heap goal
|
||||
// and the last heap goal, which tells us by how much the heap is growing and
|
||||
// shrinking. We estimate what the heap will grow to in terms of pages by taking
|
||||
// this ratio and multiplying it by heap_inuse at the end of the last GC, which
|
||||
// allows us to account for this additional fragmentation. Note that this
|
||||
// procedure makes the assumption that the degree of fragmentation won't change
|
||||
// dramatically over the next GC cycle. Overestimating the amount of
|
||||
// fragmentation simply results in higher memory use, which will be accounted
|
||||
// for by the next pacing up date. Underestimating the fragmentation however
|
||||
// could lead to performance degradation. Handling this case is not within the
|
||||
// scope of the scavenger. Situations where the amount of fragmentation balloons
|
||||
// over the course of a single GC cycle should be considered pathologies,
|
||||
// flagged as bugs, and fixed appropriately.
|
||||
//
|
||||
// An additional factor of retainExtraPercent is added as a buffer to help ensure
|
||||
// that there's more unscavenged memory to allocate out of, since each allocation
|
||||
// out of scavenged memory incurs a potentially expensive page fault.
|
||||
//
|
||||
// The goal is updated after each GC and the scavenger's pacing parameters
|
||||
// (which live in mheap_) are updated to match. The pacing parameters work much
|
||||
@@ -81,14 +103,24 @@ func heapRetained() uint64 {
|
||||
//
|
||||
// mheap_.lock must be held or the world must be stopped.
|
||||
func gcPaceScavenger() {
|
||||
// Compute our scavenging goal and align it to a physical page boundary
|
||||
// to make the following calculations more exact.
|
||||
retainedGoal := memstats.next_gc
|
||||
// If we're called before the first GC completed, disable scavenging.
|
||||
// We never scavenge before the 2nd GC cycle anyway (we don't have enough
|
||||
// information about the heap yet) so this is fine, and avoids a fault
|
||||
// or garbage data later.
|
||||
if memstats.last_next_gc == 0 {
|
||||
mheap_.scavengeBytesPerNS = 0
|
||||
return
|
||||
}
|
||||
// Compute our scavenging goal.
|
||||
goalRatio := float64(memstats.next_gc) / float64(memstats.last_next_gc)
|
||||
retainedGoal := uint64(float64(memstats.last_heap_inuse) * goalRatio)
|
||||
// Add retainExtraPercent overhead to retainedGoal. This calculation
|
||||
// looks strange but the purpose is to arrive at an integer division
|
||||
// (e.g. if retainExtraPercent = 12.5, then we get a divisor of 8)
|
||||
// that also avoids the overflow from a multiplication.
|
||||
retainedGoal += retainedGoal / (1.0 / (retainExtraPercent / 100.0))
|
||||
// Align it to a physical page boundary to make the following calculations
|
||||
// a bit more exact.
|
||||
retainedGoal = (retainedGoal + uint64(physPageSize) - 1) &^ (uint64(physPageSize) - 1)
|
||||
|
||||
// Represents where we are now in the heap's contribution to RSS in bytes.
|
||||
@@ -154,36 +186,41 @@ func gcPaceScavenger() {
|
||||
|
||||
now := nanotime()
|
||||
|
||||
lock(&scavenge.lock)
|
||||
|
||||
// Update all the pacing parameters in mheap with scavenge.lock held,
|
||||
// so that scavenge.gen is kept in sync with the updated values.
|
||||
mheap_.scavengeRetainedGoal = retainedGoal
|
||||
mheap_.scavengeRetainedBasis = retainedNow
|
||||
mheap_.scavengeTimeBasis = now
|
||||
mheap_.scavengeBytesPerNS = float64(totalWork) / float64(totalTime)
|
||||
scavenge.gen++ // increase scavenge generation
|
||||
|
||||
// Wake up background scavenger if needed, since the pacing was just updated.
|
||||
wakeScavengerLocked()
|
||||
|
||||
unlock(&scavenge.lock)
|
||||
mheap_.scavengeGen++ // increase scavenge generation
|
||||
}
|
||||
|
||||
// State of the background scavenger.
|
||||
// Sleep/wait state of the background scavenger.
|
||||
var scavenge struct {
|
||||
lock mutex
|
||||
g *g
|
||||
parked bool
|
||||
timer *timer
|
||||
gen uint32 // read with either lock or mheap_.lock, write with both
|
||||
|
||||
// Generation counter.
|
||||
//
|
||||
// It represents the last generation count (as defined by
|
||||
// mheap_.scavengeGen) checked by the scavenger and is updated
|
||||
// each time the scavenger checks whether it is on-pace.
|
||||
//
|
||||
// Skew between this field and mheap_.scavengeGen is used to
|
||||
// determine whether a new update is available.
|
||||
//
|
||||
// Protected by mheap_.lock.
|
||||
gen uint64
|
||||
}
|
||||
|
||||
// wakeScavengerLocked unparks the scavenger if necessary. It must be called
|
||||
// wakeScavenger unparks the scavenger if necessary. It must be called
|
||||
// after any pacing update.
|
||||
//
|
||||
// scavenge.lock must be held.
|
||||
func wakeScavengerLocked() {
|
||||
// mheap_.lock and scavenge.lock must not be held.
|
||||
func wakeScavenger() {
|
||||
lock(&scavenge.lock)
|
||||
if scavenge.parked {
|
||||
// Try to stop the timer but we don't really care if we succeed.
|
||||
// It's possible that either a timer was never started, or that
|
||||
@@ -198,11 +235,10 @@ func wakeScavengerLocked() {
|
||||
scavenge.parked = false
|
||||
ready(scavenge.g, 0, true)
|
||||
}
|
||||
unlock(&scavenge.lock)
|
||||
}
|
||||
|
||||
// scavengeSleep attempts to put the scavenger to sleep for ns.
|
||||
// It also checks to see if gen != scavenge.gen before going to sleep,
|
||||
// and aborts if true (meaning an update had occurred).
|
||||
//
|
||||
// Note that this function should only be called by the scavenger.
|
||||
//
|
||||
@@ -210,24 +246,32 @@ func wakeScavengerLocked() {
|
||||
// to sleep at all if there's a pending pacing change.
|
||||
//
|
||||
// Returns false if awoken early (i.e. true means a complete sleep).
|
||||
func scavengeSleep(gen uint32, ns int64) bool {
|
||||
func scavengeSleep(ns int64) bool {
|
||||
lock(&scavenge.lock)
|
||||
|
||||
// If there was an update, just abort the sleep.
|
||||
if scavenge.gen != gen {
|
||||
// First check if there's a pending update.
|
||||
// If there is one, don't bother sleeping.
|
||||
var hasUpdate bool
|
||||
systemstack(func() {
|
||||
lock(&mheap_.lock)
|
||||
hasUpdate = mheap_.scavengeGen != scavenge.gen
|
||||
unlock(&mheap_.lock)
|
||||
})
|
||||
if hasUpdate {
|
||||
unlock(&scavenge.lock)
|
||||
return false
|
||||
}
|
||||
|
||||
// Set the timer.
|
||||
//
|
||||
// This must happen here instead of inside gopark
|
||||
// because we can't close over any variables without
|
||||
// failing escape analysis.
|
||||
now := nanotime()
|
||||
scavenge.timer.when = now + ns
|
||||
startTimer(scavenge.timer)
|
||||
|
||||
// Park the goroutine. It's fine that we don't publish the
|
||||
// fact that the timer was set; even if the timer wakes up
|
||||
// and fire scavengeReady before we park, it'll block on
|
||||
// scavenge.lock.
|
||||
// Mark ourself as asleep and go to sleep.
|
||||
scavenge.parked = true
|
||||
goparkunlock(&scavenge.lock, waitReasonSleep, traceEvGoSleep, 2)
|
||||
|
||||
@@ -248,9 +292,7 @@ func bgscavenge(c chan int) {
|
||||
|
||||
scavenge.timer = new(timer)
|
||||
scavenge.timer.f = func(_ interface{}, _ uintptr) {
|
||||
lock(&scavenge.lock)
|
||||
wakeScavengerLocked()
|
||||
unlock(&scavenge.lock)
|
||||
wakeScavenger()
|
||||
}
|
||||
|
||||
c <- 1
|
||||
@@ -279,14 +321,14 @@ func bgscavenge(c chan int) {
|
||||
released := uintptr(0)
|
||||
park := false
|
||||
ttnext := int64(0)
|
||||
gen := uint32(0)
|
||||
|
||||
// Run on the system stack since we grab the heap lock,
|
||||
// and a stack growth with the heap lock means a deadlock.
|
||||
systemstack(func() {
|
||||
lock(&mheap_.lock)
|
||||
|
||||
gen = scavenge.gen
|
||||
// Update the last generation count that the scavenger has handled.
|
||||
scavenge.gen = mheap_.scavengeGen
|
||||
|
||||
// If background scavenging is disabled or if there's no work to do just park.
|
||||
retained := heapRetained()
|
||||
@@ -343,7 +385,7 @@ func bgscavenge(c chan int) {
|
||||
if released == 0 {
|
||||
// If we were unable to release anything this may be because there's
|
||||
// no free memory available to scavenge. Go to sleep and try again.
|
||||
if scavengeSleep(gen, retryDelayNS) {
|
||||
if scavengeSleep(retryDelayNS) {
|
||||
// If we successfully slept through the delay, back off exponentially.
|
||||
retryDelayNS *= 2
|
||||
}
|
||||
@@ -355,7 +397,7 @@ func bgscavenge(c chan int) {
|
||||
// If there's an appreciable amount of time until the next scavenging
|
||||
// goal, just sleep. We'll get woken up if anything changes and this
|
||||
// way we avoid spinning.
|
||||
scavengeSleep(gen, ttnext)
|
||||
scavengeSleep(ttnext)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ type mheap struct {
|
||||
scavengeRetainedBasis uint64
|
||||
scavengeBytesPerNS float64
|
||||
scavengeRetainedGoal uint64
|
||||
scavengeGen uint64 // incremented on each pacing update
|
||||
|
||||
// Page reclaimer state
|
||||
|
||||
@@ -185,6 +186,12 @@ type mheap struct {
|
||||
// simply blocking GC (by disabling preemption).
|
||||
sweepArenas []arenaIdx
|
||||
|
||||
// curArena is the arena that the heap is currently growing
|
||||
// into. This should always be physPageSize-aligned.
|
||||
curArena struct {
|
||||
base, end uintptr
|
||||
}
|
||||
|
||||
_ uint32 // ensure 64-bit alignment of central
|
||||
|
||||
// central free lists for small size classes.
|
||||
@@ -1220,16 +1227,6 @@ HaveSpan:
|
||||
// heap_released since we already did so earlier.
|
||||
sysUsed(unsafe.Pointer(s.base()), s.npages<<_PageShift)
|
||||
s.scavenged = false
|
||||
|
||||
// Since we allocated out of a scavenged span, we just
|
||||
// grew the RSS. Mitigate this by scavenging enough free
|
||||
// space to make up for it but only if we need to.
|
||||
//
|
||||
// scavengeLocked may cause coalescing, so prevent
|
||||
// coalescing with s by temporarily changing its state.
|
||||
s.state = mSpanManual
|
||||
h.scavengeIfNeededLocked(s.npages * pageSize)
|
||||
s.state = mSpanFree
|
||||
}
|
||||
|
||||
h.setSpans(s.base(), npage, s)
|
||||
@@ -1249,29 +1246,78 @@ HaveSpan:
|
||||
// h must be locked.
|
||||
func (h *mheap) grow(npage uintptr) bool {
|
||||
ask := npage << _PageShift
|
||||
v, size := h.sysAlloc(ask)
|
||||
if v == nil {
|
||||
print("runtime: out of memory: cannot allocate ", ask, "-byte block (", memstats.heap_sys, " in use)\n")
|
||||
return false
|
||||
|
||||
nBase := round(h.curArena.base+ask, physPageSize)
|
||||
if nBase > h.curArena.end {
|
||||
// Not enough room in the current arena. Allocate more
|
||||
// arena space. This may not be contiguous with the
|
||||
// current arena, so we have to request the full ask.
|
||||
av, asize := h.sysAlloc(ask)
|
||||
if av == nil {
|
||||
print("runtime: out of memory: cannot allocate ", ask, "-byte block (", memstats.heap_sys, " in use)\n")
|
||||
return false
|
||||
}
|
||||
|
||||
if uintptr(av) == h.curArena.end {
|
||||
// The new space is contiguous with the old
|
||||
// space, so just extend the current space.
|
||||
h.curArena.end = uintptr(av) + asize
|
||||
} else {
|
||||
// The new space is discontiguous. Track what
|
||||
// remains of the current space and switch to
|
||||
// the new space. This should be rare.
|
||||
if size := h.curArena.end - h.curArena.base; size != 0 {
|
||||
h.growAddSpan(unsafe.Pointer(h.curArena.base), size)
|
||||
}
|
||||
// Switch to the new space.
|
||||
h.curArena.base = uintptr(av)
|
||||
h.curArena.end = uintptr(av) + asize
|
||||
}
|
||||
|
||||
// The memory just allocated counts as both released
|
||||
// and idle, even though it's not yet backed by spans.
|
||||
//
|
||||
// The allocation is always aligned to the heap arena
|
||||
// size which is always > physPageSize, so its safe to
|
||||
// just add directly to heap_released. Coalescing, if
|
||||
// possible, will also always be correct in terms of
|
||||
// accounting, because s.base() must be a physical
|
||||
// page boundary.
|
||||
memstats.heap_released += uint64(asize)
|
||||
memstats.heap_idle += uint64(asize)
|
||||
|
||||
// Recalculate nBase
|
||||
nBase = round(h.curArena.base+ask, physPageSize)
|
||||
}
|
||||
|
||||
// Create a fake "in use" span and free it, so that the
|
||||
// right accounting and coalescing happens.
|
||||
// Grow into the current arena.
|
||||
v := h.curArena.base
|
||||
h.curArena.base = nBase
|
||||
h.growAddSpan(unsafe.Pointer(v), nBase-v)
|
||||
return true
|
||||
}
|
||||
|
||||
// growAddSpan adds a free span when the heap grows into [v, v+size).
|
||||
// This memory must be in the Prepared state (not Ready).
|
||||
//
|
||||
// h must be locked.
|
||||
func (h *mheap) growAddSpan(v unsafe.Pointer, size uintptr) {
|
||||
// Scavenge some pages to make up for the virtual memory space
|
||||
// we just allocated, but only if we need to.
|
||||
h.scavengeIfNeededLocked(size)
|
||||
|
||||
s := (*mspan)(h.spanalloc.alloc())
|
||||
s.init(uintptr(v), size/pageSize)
|
||||
h.setSpans(s.base(), s.npages, s)
|
||||
s.state = mSpanFree
|
||||
memstats.heap_idle += uint64(size)
|
||||
// (*mheap).sysAlloc returns untouched/uncommitted memory.
|
||||
// [v, v+size) is always in the Prepared state. The new span
|
||||
// must be marked scavenged so the allocator transitions it to
|
||||
// Ready when allocating from it.
|
||||
s.scavenged = true
|
||||
// s is always aligned to the heap arena size which is always > physPageSize,
|
||||
// so its totally safe to just add directly to heap_released. Coalescing,
|
||||
// if possible, will also always be correct in terms of accounting, because
|
||||
// s.base() must be a physical page boundary.
|
||||
memstats.heap_released += uint64(size)
|
||||
// This span is both released and idle, but grow already
|
||||
// updated both memstats.
|
||||
h.coalesce(s)
|
||||
h.free.insert(s)
|
||||
return true
|
||||
}
|
||||
|
||||
// Free the span back into the heap.
|
||||
|
||||
@@ -79,6 +79,8 @@ type mstats struct {
|
||||
|
||||
last_gc_nanotime uint64 // last gc (monotonic time)
|
||||
tinyallocs uint64 // number of tiny allocations that didn't cause actual allocation; not exported to go directly
|
||||
last_next_gc uint64 // next_gc for the previous GC
|
||||
last_heap_inuse uint64 // heap_inuse at mark termination of the previous GC
|
||||
|
||||
// triggerRatio is the heap growth ratio that triggers marking.
|
||||
//
|
||||
|
||||
@@ -49,6 +49,7 @@ const (
|
||||
//go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll"
|
||||
//go:cgo_import_dynamic runtime._VirtualQuery VirtualQuery%3 "kernel32.dll"
|
||||
//go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll"
|
||||
//go:cgo_import_dynamic runtime._WaitForMultipleObjects WaitForMultipleObjects%4 "kernel32.dll"
|
||||
//go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll"
|
||||
//go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll"
|
||||
|
||||
@@ -96,6 +97,7 @@ var (
|
||||
_VirtualFree,
|
||||
_VirtualQuery,
|
||||
_WaitForSingleObject,
|
||||
_WaitForMultipleObjects,
|
||||
_WriteConsoleW,
|
||||
_WriteFile,
|
||||
_ stdFunction
|
||||
@@ -139,7 +141,8 @@ func tstart_stdcall(newm *m)
|
||||
func ctrlhandler()
|
||||
|
||||
type mOS struct {
|
||||
waitsema uintptr // semaphore for parking on locks
|
||||
waitsema uintptr // semaphore for parking on locks
|
||||
resumesema uintptr // semaphore to indicate suspend/resume
|
||||
}
|
||||
|
||||
//go:linkname os_sigpipe os.sigpipe
|
||||
@@ -258,6 +261,40 @@ func loadOptionalSyscalls() {
|
||||
}
|
||||
}
|
||||
|
||||
func monitorSuspendResume() {
|
||||
const _DEVICE_NOTIFY_CALLBACK = 2
|
||||
type _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS struct {
|
||||
callback uintptr
|
||||
context uintptr
|
||||
}
|
||||
|
||||
powrprof := windowsLoadSystemLib([]byte("powrprof.dll\000"))
|
||||
if powrprof == 0 {
|
||||
return // Running on Windows 7, where we don't need it anyway.
|
||||
}
|
||||
powerRegisterSuspendResumeNotification := windowsFindfunc(powrprof, []byte("PowerRegisterSuspendResumeNotification\000"))
|
||||
if powerRegisterSuspendResumeNotification == nil {
|
||||
return // Running on Windows 7, where we don't need it anyway.
|
||||
}
|
||||
var fn interface{} = func(context uintptr, changeType uint32, setting uintptr) uintptr {
|
||||
for mp := (*m)(atomic.Loadp(unsafe.Pointer(&allm))); mp != nil; mp = mp.alllink {
|
||||
if mp.resumesema != 0 {
|
||||
stdcall1(_SetEvent, mp.resumesema)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
params := _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS{
|
||||
callback: compileCallback(*efaceOf(&fn), true),
|
||||
}
|
||||
handle := uintptr(0)
|
||||
if stdcall3(powerRegisterSuspendResumeNotification, _DEVICE_NOTIFY_CALLBACK,
|
||||
uintptr(unsafe.Pointer(¶ms)),
|
||||
uintptr(unsafe.Pointer(&handle))) != 0 {
|
||||
throw("PowerRegisterSuspendResumeNotification failure")
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func getLoadLibrary() uintptr {
|
||||
return uintptr(unsafe.Pointer(_LoadLibraryW))
|
||||
@@ -488,6 +525,10 @@ func goenvs() {
|
||||
}
|
||||
|
||||
stdcall1(_FreeEnvironmentStringsW, uintptr(strings))
|
||||
|
||||
// We call this all the way here, late in init, so that malloc works
|
||||
// for the callback function this generates.
|
||||
monitorSuspendResume()
|
||||
}
|
||||
|
||||
// exiting is set to non-zero when the process is exiting.
|
||||
@@ -606,19 +647,32 @@ func semasleep(ns int64) int32 {
|
||||
_WAIT_FAILED = 0xFFFFFFFF
|
||||
)
|
||||
|
||||
// store ms in ns to save stack space
|
||||
var result uintptr
|
||||
if ns < 0 {
|
||||
ns = _INFINITE
|
||||
result = stdcall2(_WaitForSingleObject, getg().m.waitsema, uintptr(_INFINITE))
|
||||
} else {
|
||||
ns = int64(timediv(ns, 1000000, nil))
|
||||
if ns == 0 {
|
||||
ns = 1
|
||||
start := nanotime()
|
||||
elapsed := int64(0)
|
||||
for {
|
||||
ms := int64(timediv(ns-elapsed, 1000000, nil))
|
||||
if ms == 0 {
|
||||
ms = 1
|
||||
}
|
||||
result = stdcall4(_WaitForMultipleObjects, 2,
|
||||
uintptr(unsafe.Pointer(&[2]uintptr{getg().m.waitsema, getg().m.resumesema})),
|
||||
0, uintptr(ms))
|
||||
if result != _WAIT_OBJECT_0+1 {
|
||||
// Not a suspend/resume event
|
||||
break
|
||||
}
|
||||
elapsed = nanotime() - start
|
||||
if elapsed >= ns {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result := stdcall2(_WaitForSingleObject, getg().m.waitsema, uintptr(ns))
|
||||
switch result {
|
||||
case _WAIT_OBJECT_0: //signaled
|
||||
case _WAIT_OBJECT_0: // Signaled
|
||||
return 0
|
||||
|
||||
case _WAIT_TIMEOUT:
|
||||
@@ -667,6 +721,15 @@ func semacreate(mp *m) {
|
||||
throw("runtime.semacreate")
|
||||
})
|
||||
}
|
||||
mp.resumesema = stdcall4(_CreateEventA, 0, 0, 0, 0)
|
||||
if mp.resumesema == 0 {
|
||||
systemstack(func() {
|
||||
print("runtime: createevent failed; errno=", getlasterror(), "\n")
|
||||
throw("runtime.semacreate")
|
||||
})
|
||||
stdcall1(_CloseHandle, mp.waitsema)
|
||||
mp.waitsema = 0
|
||||
}
|
||||
}
|
||||
|
||||
// May run with m.p==nil, so write barriers are not allowed. This
|
||||
|
||||
@@ -60,7 +60,7 @@ func libcCall(fn, arg unsafe.Pointer) int32 {
|
||||
//go:nosplit
|
||||
//go:cgo_unsafe_args
|
||||
func syscall_syscall(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
|
||||
entersyscallblock()
|
||||
entersyscall()
|
||||
libcCall(unsafe.Pointer(funcPC(syscall)), unsafe.Pointer(&fn))
|
||||
exitsyscall()
|
||||
return
|
||||
@@ -71,7 +71,7 @@ func syscall()
|
||||
//go:nosplit
|
||||
//go:cgo_unsafe_args
|
||||
func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
|
||||
entersyscallblock()
|
||||
entersyscall()
|
||||
libcCall(unsafe.Pointer(funcPC(syscall6)), unsafe.Pointer(&fn))
|
||||
exitsyscall()
|
||||
return
|
||||
@@ -82,7 +82,7 @@ func syscall6()
|
||||
//go:nosplit
|
||||
//go:cgo_unsafe_args
|
||||
func syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
|
||||
entersyscallblock()
|
||||
entersyscall()
|
||||
libcCall(unsafe.Pointer(funcPC(syscall6X)), unsafe.Pointer(&fn))
|
||||
exitsyscall()
|
||||
return
|
||||
@@ -93,7 +93,7 @@ func syscall6X()
|
||||
//go:nosplit
|
||||
//go:cgo_unsafe_args
|
||||
func syscall_syscallPtr(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
|
||||
entersyscallblock()
|
||||
entersyscall()
|
||||
libcCall(unsafe.Pointer(funcPC(syscallPtr)), unsafe.Pointer(&fn))
|
||||
exitsyscall()
|
||||
return
|
||||
|
||||
@@ -74,16 +74,18 @@ func compileCallback(fn eface, cleanstack bool) (code uintptr) {
|
||||
argsize += uintptrSize
|
||||
}
|
||||
|
||||
lock(&cbs.lock)
|
||||
defer unlock(&cbs.lock)
|
||||
lock(&cbs.lock) // We don't unlock this in a defer because this is used from the system stack.
|
||||
|
||||
n := cbs.n
|
||||
for i := 0; i < n; i++ {
|
||||
if cbs.ctxt[i].gobody == fn.data && cbs.ctxt[i].isCleanstack() == cleanstack {
|
||||
return callbackasmAddr(i)
|
||||
r := callbackasmAddr(i)
|
||||
unlock(&cbs.lock)
|
||||
return r
|
||||
}
|
||||
}
|
||||
if n >= cb_max {
|
||||
unlock(&cbs.lock)
|
||||
throw("too many callback functions")
|
||||
}
|
||||
|
||||
@@ -99,7 +101,9 @@ func compileCallback(fn eface, cleanstack bool) (code uintptr) {
|
||||
cbs.ctxt[n] = c
|
||||
cbs.n++
|
||||
|
||||
return callbackasmAddr(n)
|
||||
r := callbackasmAddr(n)
|
||||
unlock(&cbs.lock)
|
||||
return r
|
||||
}
|
||||
|
||||
const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
|
||||
|
||||
@@ -643,7 +643,7 @@ func atof64(s string) (f float64, err error) {
|
||||
// ParseFloat returns the nearest floating-point number rounded
|
||||
// using IEEE754 unbiased rounding.
|
||||
// (Parsing a hexadecimal floating-point value only rounds when
|
||||
// there are more bits in the hexadecimal representatiton than
|
||||
// there are more bits in the hexadecimal representation than
|
||||
// will fit in the mantissa.)
|
||||
//
|
||||
// The errors that ParseFloat returns have concrete type *NumError
|
||||
|
||||
@@ -154,7 +154,8 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) {
|
||||
//
|
||||
// If base == 0, the base is implied by the string's prefix:
|
||||
// base 2 for "0b", base 8 for "0" or "0o", base 16 for "0x",
|
||||
// and base 10 otherwise.
|
||||
// and base 10 otherwise. Also, for base == 0 only, underscore
|
||||
// characters are permitted per the Go integer literal syntax.
|
||||
// If base is below 0, is 1, or is above 36, an error is returned.
|
||||
//
|
||||
// The bitSize argument specifies the integer type
|
||||
|
||||
@@ -4,897 +4,23 @@
|
||||
|
||||
// +build ignore
|
||||
|
||||
/*
|
||||
mksyscall_windows generates windows system call bodies
|
||||
|
||||
It parses all files specified on command line containing function
|
||||
prototypes (like syscall_windows.go) and prints system call bodies
|
||||
to standard output.
|
||||
|
||||
The prototypes are marked by lines beginning with "//sys" and read
|
||||
like func declarations if //sys is replaced by func, but:
|
||||
|
||||
* The parameter lists must give a name for each argument. This
|
||||
includes return parameters.
|
||||
|
||||
* The parameter lists must give a type for each argument:
|
||||
the (x, y, z int) shorthand is not allowed.
|
||||
|
||||
* If the return parameter is an error number, it must be named err.
|
||||
|
||||
* If go func name needs to be different from its winapi dll name,
|
||||
the winapi name could be specified at the end, after "=" sign, like
|
||||
//sys LoadLibrary(libname string) (handle uint32, err error) = LoadLibraryA
|
||||
|
||||
* Each function that returns err needs to supply a condition, that
|
||||
return value of winapi will be tested against to detect failure.
|
||||
This would set err to windows "last-error", otherwise it will be nil.
|
||||
The value can be provided at end of //sys declaration, like
|
||||
//sys LoadLibrary(libname string) (handle uint32, err error) [failretval==-1] = LoadLibraryA
|
||||
and is [failretval==0] by default.
|
||||
|
||||
Usage:
|
||||
mksyscall_windows [flags] [path ...]
|
||||
|
||||
The flags are:
|
||||
-output
|
||||
Specify output file name (outputs to console if blank).
|
||||
-trace
|
||||
Generate print statement after every syscall.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var (
|
||||
filename = flag.String("output", "", "output file name (standard output if omitted)")
|
||||
printTraceFlag = flag.Bool("trace", false, "generate print statement after every syscall")
|
||||
systemDLL = flag.Bool("systemdll", true, "whether all DLLs should be loaded from the Windows system directory")
|
||||
)
|
||||
|
||||
func trim(s string) string {
|
||||
return strings.Trim(s, " \t")
|
||||
}
|
||||
|
||||
var packageName string
|
||||
|
||||
func packagename() string {
|
||||
return packageName
|
||||
}
|
||||
|
||||
func syscalldot() string {
|
||||
if packageName == "syscall" {
|
||||
return ""
|
||||
}
|
||||
return "syscall."
|
||||
}
|
||||
|
||||
// Param is function parameter
|
||||
type Param struct {
|
||||
Name string
|
||||
Type string
|
||||
fn *Fn
|
||||
tmpVarIdx int
|
||||
}
|
||||
|
||||
// tmpVar returns temp variable name that will be used to represent p during syscall.
|
||||
func (p *Param) tmpVar() string {
|
||||
if p.tmpVarIdx < 0 {
|
||||
p.tmpVarIdx = p.fn.curTmpVarIdx
|
||||
p.fn.curTmpVarIdx++
|
||||
}
|
||||
return fmt.Sprintf("_p%d", p.tmpVarIdx)
|
||||
}
|
||||
|
||||
// BoolTmpVarCode returns source code for bool temp variable.
|
||||
func (p *Param) BoolTmpVarCode() string {
|
||||
const code = `var %s uint32
|
||||
if %s {
|
||||
%s = 1
|
||||
} else {
|
||||
%s = 0
|
||||
}`
|
||||
tmp := p.tmpVar()
|
||||
return fmt.Sprintf(code, tmp, p.Name, tmp, tmp)
|
||||
}
|
||||
|
||||
// SliceTmpVarCode returns source code for slice temp variable.
|
||||
func (p *Param) SliceTmpVarCode() string {
|
||||
const code = `var %s *%s
|
||||
if len(%s) > 0 {
|
||||
%s = &%s[0]
|
||||
}`
|
||||
tmp := p.tmpVar()
|
||||
return fmt.Sprintf(code, tmp, p.Type[2:], p.Name, tmp, p.Name)
|
||||
}
|
||||
|
||||
// StringTmpVarCode returns source code for string temp variable.
|
||||
func (p *Param) StringTmpVarCode() string {
|
||||
errvar := p.fn.Rets.ErrorVarName()
|
||||
if errvar == "" {
|
||||
errvar = "_"
|
||||
}
|
||||
tmp := p.tmpVar()
|
||||
const code = `var %s %s
|
||||
%s, %s = %s(%s)`
|
||||
s := fmt.Sprintf(code, tmp, p.fn.StrconvType(), tmp, errvar, p.fn.StrconvFunc(), p.Name)
|
||||
if errvar == "-" {
|
||||
return s
|
||||
}
|
||||
const morecode = `
|
||||
if %s != nil {
|
||||
return
|
||||
}`
|
||||
return s + fmt.Sprintf(morecode, errvar)
|
||||
}
|
||||
|
||||
// TmpVarCode returns source code for temp variable.
|
||||
func (p *Param) TmpVarCode() string {
|
||||
switch {
|
||||
case p.Type == "bool":
|
||||
return p.BoolTmpVarCode()
|
||||
case strings.HasPrefix(p.Type, "[]"):
|
||||
return p.SliceTmpVarCode()
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// TmpVarHelperCode returns source code for helper's temp variable.
|
||||
func (p *Param) TmpVarHelperCode() string {
|
||||
if p.Type != "string" {
|
||||
return ""
|
||||
}
|
||||
return p.StringTmpVarCode()
|
||||
}
|
||||
|
||||
// SyscallArgList returns source code fragments representing p parameter
|
||||
// in syscall. Slices are translated into 2 syscall parameters: pointer to
|
||||
// the first element and length.
|
||||
func (p *Param) SyscallArgList() []string {
|
||||
t := p.HelperType()
|
||||
var s string
|
||||
switch {
|
||||
case t[0] == '*':
|
||||
s = fmt.Sprintf("unsafe.Pointer(%s)", p.Name)
|
||||
case t == "bool":
|
||||
s = p.tmpVar()
|
||||
case strings.HasPrefix(t, "[]"):
|
||||
return []string{
|
||||
fmt.Sprintf("uintptr(unsafe.Pointer(%s))", p.tmpVar()),
|
||||
fmt.Sprintf("uintptr(len(%s))", p.Name),
|
||||
}
|
||||
default:
|
||||
s = p.Name
|
||||
}
|
||||
return []string{fmt.Sprintf("uintptr(%s)", s)}
|
||||
}
|
||||
|
||||
// IsError determines if p parameter is used to return error.
|
||||
func (p *Param) IsError() bool {
|
||||
return p.Name == "err" && p.Type == "error"
|
||||
}
|
||||
|
||||
// HelperType returns type of parameter p used in helper function.
|
||||
func (p *Param) HelperType() string {
|
||||
if p.Type == "string" {
|
||||
return p.fn.StrconvType()
|
||||
}
|
||||
return p.Type
|
||||
}
|
||||
|
||||
// join concatenates parameters ps into a string with sep separator.
|
||||
// Each parameter is converted into string by applying fn to it
|
||||
// before conversion.
|
||||
func join(ps []*Param, fn func(*Param) string, sep string) string {
|
||||
if len(ps) == 0 {
|
||||
return ""
|
||||
}
|
||||
a := make([]string, 0)
|
||||
for _, p := range ps {
|
||||
a = append(a, fn(p))
|
||||
}
|
||||
return strings.Join(a, sep)
|
||||
}
|
||||
|
||||
// Rets describes function return parameters.
|
||||
type Rets struct {
|
||||
Name string
|
||||
Type string
|
||||
ReturnsError bool
|
||||
FailCond string
|
||||
}
|
||||
|
||||
// ErrorVarName returns error variable name for r.
|
||||
func (r *Rets) ErrorVarName() string {
|
||||
if r.ReturnsError {
|
||||
return "err"
|
||||
}
|
||||
if r.Type == "error" {
|
||||
return r.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ToParams converts r into slice of *Param.
|
||||
func (r *Rets) ToParams() []*Param {
|
||||
ps := make([]*Param, 0)
|
||||
if len(r.Name) > 0 {
|
||||
ps = append(ps, &Param{Name: r.Name, Type: r.Type})
|
||||
}
|
||||
if r.ReturnsError {
|
||||
ps = append(ps, &Param{Name: "err", Type: "error"})
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
// List returns source code of syscall return parameters.
|
||||
func (r *Rets) List() string {
|
||||
s := join(r.ToParams(), func(p *Param) string { return p.Name + " " + p.Type }, ", ")
|
||||
if len(s) > 0 {
|
||||
s = "(" + s + ")"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// PrintList returns source code of trace printing part correspondent
|
||||
// to syscall return values.
|
||||
func (r *Rets) PrintList() string {
|
||||
return join(r.ToParams(), func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `)
|
||||
}
|
||||
|
||||
// SetReturnValuesCode returns source code that accepts syscall return values.
|
||||
func (r *Rets) SetReturnValuesCode() string {
|
||||
if r.Name == "" && !r.ReturnsError {
|
||||
return ""
|
||||
}
|
||||
retvar := "r0"
|
||||
if r.Name == "" {
|
||||
retvar = "r1"
|
||||
}
|
||||
errvar := "_"
|
||||
if r.ReturnsError {
|
||||
errvar = "e1"
|
||||
}
|
||||
return fmt.Sprintf("%s, _, %s := ", retvar, errvar)
|
||||
}
|
||||
|
||||
func (r *Rets) useLongHandleErrorCode(retvar string) string {
|
||||
const code = `if %s {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = %sEINVAL
|
||||
}
|
||||
}`
|
||||
cond := retvar + " == 0"
|
||||
if r.FailCond != "" {
|
||||
cond = strings.Replace(r.FailCond, "failretval", retvar, 1)
|
||||
}
|
||||
return fmt.Sprintf(code, cond, syscalldot())
|
||||
}
|
||||
|
||||
// SetErrorCode returns source code that sets return parameters.
|
||||
func (r *Rets) SetErrorCode() string {
|
||||
const code = `if r0 != 0 {
|
||||
%s = %sErrno(r0)
|
||||
}`
|
||||
if r.Name == "" && !r.ReturnsError {
|
||||
return ""
|
||||
}
|
||||
if r.Name == "" {
|
||||
return r.useLongHandleErrorCode("r1")
|
||||
}
|
||||
if r.Type == "error" {
|
||||
return fmt.Sprintf(code, r.Name, syscalldot())
|
||||
}
|
||||
s := ""
|
||||
switch {
|
||||
case r.Type[0] == '*':
|
||||
s = fmt.Sprintf("%s = (%s)(unsafe.Pointer(r0))", r.Name, r.Type)
|
||||
case r.Type == "bool":
|
||||
s = fmt.Sprintf("%s = r0 != 0", r.Name)
|
||||
default:
|
||||
s = fmt.Sprintf("%s = %s(r0)", r.Name, r.Type)
|
||||
}
|
||||
if !r.ReturnsError {
|
||||
return s
|
||||
}
|
||||
return s + "\n\t" + r.useLongHandleErrorCode(r.Name)
|
||||
}
|
||||
|
||||
// Fn describes syscall function.
|
||||
type Fn struct {
|
||||
Name string
|
||||
Params []*Param
|
||||
Rets *Rets
|
||||
PrintTrace bool
|
||||
dllname string
|
||||
dllfuncname string
|
||||
src string
|
||||
// TODO: get rid of this field and just use parameter index instead
|
||||
curTmpVarIdx int // insure tmp variables have uniq names
|
||||
}
|
||||
|
||||
// extractParams parses s to extract function parameters.
|
||||
func extractParams(s string, f *Fn) ([]*Param, error) {
|
||||
s = trim(s)
|
||||
if s == "" {
|
||||
return nil, nil
|
||||
}
|
||||
a := strings.Split(s, ",")
|
||||
ps := make([]*Param, len(a))
|
||||
for i := range ps {
|
||||
s2 := trim(a[i])
|
||||
b := strings.Split(s2, " ")
|
||||
if len(b) != 2 {
|
||||
b = strings.Split(s2, "\t")
|
||||
if len(b) != 2 {
|
||||
return nil, errors.New("Could not extract function parameter from \"" + s2 + "\"")
|
||||
}
|
||||
}
|
||||
ps[i] = &Param{
|
||||
Name: trim(b[0]),
|
||||
Type: trim(b[1]),
|
||||
fn: f,
|
||||
tmpVarIdx: -1,
|
||||
}
|
||||
}
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
// extractSection extracts text out of string s starting after start
|
||||
// and ending just before end. found return value will indicate success,
|
||||
// and prefix, body and suffix will contain correspondent parts of string s.
|
||||
func extractSection(s string, start, end rune) (prefix, body, suffix string, found bool) {
|
||||
s = trim(s)
|
||||
if strings.HasPrefix(s, string(start)) {
|
||||
// no prefix
|
||||
body = s[1:]
|
||||
} else {
|
||||
a := strings.SplitN(s, string(start), 2)
|
||||
if len(a) != 2 {
|
||||
return "", "", s, false
|
||||
}
|
||||
prefix = a[0]
|
||||
body = a[1]
|
||||
}
|
||||
a := strings.SplitN(body, string(end), 2)
|
||||
if len(a) != 2 {
|
||||
return "", "", "", false
|
||||
}
|
||||
return prefix, a[0], a[1], true
|
||||
}
|
||||
|
||||
// newFn parses string s and return created function Fn.
|
||||
func newFn(s string) (*Fn, error) {
|
||||
s = trim(s)
|
||||
f := &Fn{
|
||||
Rets: &Rets{},
|
||||
src: s,
|
||||
PrintTrace: *printTraceFlag,
|
||||
}
|
||||
// function name and args
|
||||
prefix, body, s, found := extractSection(s, '(', ')')
|
||||
if !found || prefix == "" {
|
||||
return nil, errors.New("Could not extract function name and parameters from \"" + f.src + "\"")
|
||||
}
|
||||
f.Name = prefix
|
||||
var err error
|
||||
f.Params, err = extractParams(body, f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// return values
|
||||
_, body, s, found = extractSection(s, '(', ')')
|
||||
if found {
|
||||
r, err := extractParams(body, f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch len(r) {
|
||||
case 0:
|
||||
case 1:
|
||||
if r[0].IsError() {
|
||||
f.Rets.ReturnsError = true
|
||||
} else {
|
||||
f.Rets.Name = r[0].Name
|
||||
f.Rets.Type = r[0].Type
|
||||
}
|
||||
case 2:
|
||||
if !r[1].IsError() {
|
||||
return nil, errors.New("Only last windows error is allowed as second return value in \"" + f.src + "\"")
|
||||
}
|
||||
f.Rets.ReturnsError = true
|
||||
f.Rets.Name = r[0].Name
|
||||
f.Rets.Type = r[0].Type
|
||||
default:
|
||||
return nil, errors.New("Too many return values in \"" + f.src + "\"")
|
||||
}
|
||||
}
|
||||
// fail condition
|
||||
_, body, s, found = extractSection(s, '[', ']')
|
||||
if found {
|
||||
f.Rets.FailCond = body
|
||||
}
|
||||
// dll and dll function names
|
||||
s = trim(s)
|
||||
if s == "" {
|
||||
return f, nil
|
||||
}
|
||||
if !strings.HasPrefix(s, "=") {
|
||||
return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
|
||||
}
|
||||
s = trim(s[1:])
|
||||
a := strings.Split(s, ".")
|
||||
switch len(a) {
|
||||
case 1:
|
||||
f.dllfuncname = a[0]
|
||||
case 2:
|
||||
f.dllname = a[0]
|
||||
f.dllfuncname = a[1]
|
||||
default:
|
||||
return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// DLLName returns DLL name for function f.
|
||||
func (f *Fn) DLLName() string {
|
||||
if f.dllname == "" {
|
||||
return "kernel32"
|
||||
}
|
||||
return f.dllname
|
||||
}
|
||||
|
||||
// DLLName returns DLL function name for function f.
|
||||
func (f *Fn) DLLFuncName() string {
|
||||
if f.dllfuncname == "" {
|
||||
return f.Name
|
||||
}
|
||||
return f.dllfuncname
|
||||
}
|
||||
|
||||
// ParamList returns source code for function f parameters.
|
||||
func (f *Fn) ParamList() string {
|
||||
return join(f.Params, func(p *Param) string { return p.Name + " " + p.Type }, ", ")
|
||||
}
|
||||
|
||||
// HelperParamList returns source code for helper function f parameters.
|
||||
func (f *Fn) HelperParamList() string {
|
||||
return join(f.Params, func(p *Param) string { return p.Name + " " + p.HelperType() }, ", ")
|
||||
}
|
||||
|
||||
// ParamPrintList returns source code of trace printing part correspondent
|
||||
// to syscall input parameters.
|
||||
func (f *Fn) ParamPrintList() string {
|
||||
return join(f.Params, func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `)
|
||||
}
|
||||
|
||||
// ParamCount return number of syscall parameters for function f.
|
||||
func (f *Fn) ParamCount() int {
|
||||
n := 0
|
||||
for _, p := range f.Params {
|
||||
n += len(p.SyscallArgList())
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// SyscallParamCount determines which version of Syscall/Syscall6/Syscall9/...
|
||||
// to use. It returns parameter count for correspondent SyscallX function.
|
||||
func (f *Fn) SyscallParamCount() int {
|
||||
n := f.ParamCount()
|
||||
switch {
|
||||
case n <= 3:
|
||||
return 3
|
||||
case n <= 6:
|
||||
return 6
|
||||
case n <= 9:
|
||||
return 9
|
||||
case n <= 12:
|
||||
return 12
|
||||
case n <= 15:
|
||||
return 15
|
||||
default:
|
||||
panic("too many arguments to system call")
|
||||
}
|
||||
}
|
||||
|
||||
// Syscall determines which SyscallX function to use for function f.
|
||||
func (f *Fn) Syscall() string {
|
||||
c := f.SyscallParamCount()
|
||||
if c == 3 {
|
||||
return syscalldot() + "Syscall"
|
||||
}
|
||||
return syscalldot() + "Syscall" + strconv.Itoa(c)
|
||||
}
|
||||
|
||||
// SyscallParamList returns source code for SyscallX parameters for function f.
|
||||
func (f *Fn) SyscallParamList() string {
|
||||
a := make([]string, 0)
|
||||
for _, p := range f.Params {
|
||||
a = append(a, p.SyscallArgList()...)
|
||||
}
|
||||
for len(a) < f.SyscallParamCount() {
|
||||
a = append(a, "0")
|
||||
}
|
||||
return strings.Join(a, ", ")
|
||||
}
|
||||
|
||||
// HelperCallParamList returns source code of call into function f helper.
|
||||
func (f *Fn) HelperCallParamList() string {
|
||||
a := make([]string, 0, len(f.Params))
|
||||
for _, p := range f.Params {
|
||||
s := p.Name
|
||||
if p.Type == "string" {
|
||||
s = p.tmpVar()
|
||||
}
|
||||
a = append(a, s)
|
||||
}
|
||||
return strings.Join(a, ", ")
|
||||
}
|
||||
|
||||
// IsUTF16 is true, if f is W (utf16) function. It is false
|
||||
// for all A (ascii) functions.
|
||||
func (f *Fn) IsUTF16() bool {
|
||||
s := f.DLLFuncName()
|
||||
return s[len(s)-1] == 'W'
|
||||
}
|
||||
|
||||
// StrconvFunc returns name of Go string to OS string function for f.
|
||||
func (f *Fn) StrconvFunc() string {
|
||||
if f.IsUTF16() {
|
||||
return syscalldot() + "UTF16PtrFromString"
|
||||
}
|
||||
return syscalldot() + "BytePtrFromString"
|
||||
}
|
||||
|
||||
// StrconvType returns Go type name used for OS string for f.
|
||||
func (f *Fn) StrconvType() string {
|
||||
if f.IsUTF16() {
|
||||
return "*uint16"
|
||||
}
|
||||
return "*byte"
|
||||
}
|
||||
|
||||
// HasStringParam is true, if f has at least one string parameter.
|
||||
// Otherwise it is false.
|
||||
func (f *Fn) HasStringParam() bool {
|
||||
for _, p := range f.Params {
|
||||
if p.Type == "string" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HelperName returns name of function f helper.
|
||||
func (f *Fn) HelperName() string {
|
||||
if !f.HasStringParam() {
|
||||
return f.Name
|
||||
}
|
||||
return "_" + f.Name
|
||||
}
|
||||
|
||||
// Source files and functions.
|
||||
type Source struct {
|
||||
Funcs []*Fn
|
||||
Files []string
|
||||
StdLibImports []string
|
||||
ExternalImports []string
|
||||
}
|
||||
|
||||
func (src *Source) Import(pkg string) {
|
||||
src.StdLibImports = append(src.StdLibImports, pkg)
|
||||
sort.Strings(src.StdLibImports)
|
||||
}
|
||||
|
||||
func (src *Source) ExternalImport(pkg string) {
|
||||
src.ExternalImports = append(src.ExternalImports, pkg)
|
||||
sort.Strings(src.ExternalImports)
|
||||
}
|
||||
|
||||
// ParseFiles parses files listed in fs and extracts all syscall
|
||||
// functions listed in sys comments. It returns source files
|
||||
// and functions collection *Source if successful.
|
||||
func ParseFiles(fs []string) (*Source, error) {
|
||||
src := &Source{
|
||||
Funcs: make([]*Fn, 0),
|
||||
Files: make([]string, 0),
|
||||
StdLibImports: []string{
|
||||
"unsafe",
|
||||
},
|
||||
ExternalImports: make([]string, 0),
|
||||
}
|
||||
for _, file := range fs {
|
||||
if err := src.ParseFile(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return src, nil
|
||||
}
|
||||
|
||||
// DLLs return dll names for a source set src.
|
||||
func (src *Source) DLLs() []string {
|
||||
uniq := make(map[string]bool)
|
||||
r := make([]string, 0)
|
||||
for _, f := range src.Funcs {
|
||||
name := f.DLLName()
|
||||
if _, found := uniq[name]; !found {
|
||||
uniq[name] = true
|
||||
r = append(r, name)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// ParseFile adds additional file path to a source set src.
|
||||
func (src *Source) ParseFile(path string) error {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
s := bufio.NewScanner(file)
|
||||
for s.Scan() {
|
||||
t := trim(s.Text())
|
||||
if len(t) < 7 {
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(t, "//sys") {
|
||||
continue
|
||||
}
|
||||
t = t[5:]
|
||||
if !(t[0] == ' ' || t[0] == '\t') {
|
||||
continue
|
||||
}
|
||||
f, err := newFn(t[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
src.Funcs = append(src.Funcs, f)
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
src.Files = append(src.Files, path)
|
||||
|
||||
// get package name
|
||||
fset := token.NewFileSet()
|
||||
_, err = file.Seek(0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg, err := parser.ParseFile(fset, "", file, parser.PackageClauseOnly)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
packageName = pkg.Name.Name
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsStdRepo reports whether src is part of standard library.
|
||||
func (src *Source) IsStdRepo() (bool, error) {
|
||||
if len(src.Files) == 0 {
|
||||
return false, errors.New("no input files provided")
|
||||
}
|
||||
abspath, err := filepath.Abs(src.Files[0])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
goroot := runtime.GOROOT()
|
||||
if runtime.GOOS == "windows" {
|
||||
abspath = strings.ToLower(abspath)
|
||||
goroot = strings.ToLower(goroot)
|
||||
}
|
||||
sep := string(os.PathSeparator)
|
||||
if !strings.HasSuffix(goroot, sep) {
|
||||
goroot += sep
|
||||
}
|
||||
return strings.HasPrefix(abspath, goroot), nil
|
||||
}
|
||||
|
||||
// Generate output source file from a source set src.
|
||||
func (src *Source) Generate(w io.Writer) error {
|
||||
const (
|
||||
pkgStd = iota // any package in std library
|
||||
pkgXSysWindows // x/sys/windows package
|
||||
pkgOther
|
||||
)
|
||||
isStdRepo, err := src.IsStdRepo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var pkgtype int
|
||||
switch {
|
||||
case isStdRepo:
|
||||
pkgtype = pkgStd
|
||||
case packageName == "windows":
|
||||
// TODO: this needs better logic than just using package name
|
||||
pkgtype = pkgXSysWindows
|
||||
default:
|
||||
pkgtype = pkgOther
|
||||
}
|
||||
if *systemDLL {
|
||||
switch pkgtype {
|
||||
case pkgStd:
|
||||
src.Import("internal/syscall/windows/sysdll")
|
||||
case pkgXSysWindows:
|
||||
default:
|
||||
src.ExternalImport("golang.org/x/sys/windows")
|
||||
}
|
||||
}
|
||||
if packageName != "syscall" {
|
||||
src.Import("syscall")
|
||||
}
|
||||
funcMap := template.FuncMap{
|
||||
"packagename": packagename,
|
||||
"syscalldot": syscalldot,
|
||||
"newlazydll": func(dll string) string {
|
||||
arg := "\"" + dll + ".dll\""
|
||||
if !*systemDLL {
|
||||
return syscalldot() + "NewLazyDLL(" + arg + ")"
|
||||
}
|
||||
switch pkgtype {
|
||||
case pkgStd:
|
||||
return syscalldot() + "NewLazyDLL(sysdll.Add(" + arg + "))"
|
||||
case pkgXSysWindows:
|
||||
return "NewLazySystemDLL(" + arg + ")"
|
||||
default:
|
||||
return "windows.NewLazySystemDLL(" + arg + ")"
|
||||
}
|
||||
},
|
||||
}
|
||||
t := template.Must(template.New("main").Funcs(funcMap).Parse(srcTemplate))
|
||||
err = t.Execute(w, src)
|
||||
if err != nil {
|
||||
return errors.New("Failed to execute template: " + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: mksyscall_windows [flags] [path ...]\n")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
if len(flag.Args()) <= 0 {
|
||||
fmt.Fprintf(os.Stderr, "no files to parse provided\n")
|
||||
usage()
|
||||
}
|
||||
|
||||
src, err := ParseFiles(flag.Args())
|
||||
os.Stderr.WriteString("WARNING: Please switch from using:\n go run $GOROOT/src/syscall/mksyscall_windows.go\nto using:\n go run golang.org/x/sys/windows/mkwinsyscall\n")
|
||||
args := append([]string{"run", "golang.org/x/sys/windows/mkwinsyscall"}, os.Args[1:]...)
|
||||
cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := src.Generate(&buf); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
data, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if *filename == "" {
|
||||
_, err = os.Stdout.Write(data)
|
||||
} else {
|
||||
err = ioutil.WriteFile(*filename, data, 0644)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: use println instead to print in the following template
|
||||
const srcTemplate = `
|
||||
|
||||
{{define "main"}}// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package {{packagename}}
|
||||
|
||||
import (
|
||||
{{range .StdLibImports}}"{{.}}"
|
||||
{{end}}
|
||||
|
||||
{{range .ExternalImports}}"{{.}}"
|
||||
{{end}}
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = {{syscalldot}}Errno(errnoERROR_IO_PENDING)
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e {{syscalldot}}Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return nil
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
{{template "dlls" .}}
|
||||
{{template "funcnames" .}})
|
||||
{{range .Funcs}}{{if .HasStringParam}}{{template "helperbody" .}}{{end}}{{template "funcbody" .}}{{end}}
|
||||
{{end}}
|
||||
|
||||
{{/* help functions */}}
|
||||
|
||||
{{define "dlls"}}{{range .DLLs}} mod{{.}} = {{newlazydll .}}
|
||||
{{end}}{{end}}
|
||||
|
||||
{{define "funcnames"}}{{range .Funcs}} proc{{.DLLFuncName}} = mod{{.DLLName}}.NewProc("{{.DLLFuncName}}")
|
||||
{{end}}{{end}}
|
||||
|
||||
{{define "helperbody"}}
|
||||
func {{.Name}}({{.ParamList}}) {{template "results" .}}{
|
||||
{{template "helpertmpvars" .}} return {{.HelperName}}({{.HelperCallParamList}})
|
||||
}
|
||||
{{end}}
|
||||
|
||||
{{define "funcbody"}}
|
||||
func {{.HelperName}}({{.HelperParamList}}) {{template "results" .}}{
|
||||
{{template "tmpvars" .}} {{template "syscall" .}}
|
||||
{{template "seterror" .}}{{template "printtrace" .}} return
|
||||
}
|
||||
{{end}}
|
||||
|
||||
{{define "helpertmpvars"}}{{range .Params}}{{if .TmpVarHelperCode}} {{.TmpVarHelperCode}}
|
||||
{{end}}{{end}}{{end}}
|
||||
|
||||
{{define "tmpvars"}}{{range .Params}}{{if .TmpVarCode}} {{.TmpVarCode}}
|
||||
{{end}}{{end}}{{end}}
|
||||
|
||||
{{define "results"}}{{if .Rets.List}}{{.Rets.List}} {{end}}{{end}}
|
||||
|
||||
{{define "syscall"}}{{.Rets.SetReturnValuesCode}}{{.Syscall}}(proc{{.DLLFuncName}}.Addr(), {{.ParamCount}}, {{.SyscallParamList}}){{end}}
|
||||
|
||||
{{define "seterror"}}{{if .Rets.SetErrorCode}} {{.Rets.SetErrorCode}}
|
||||
{{end}}{{end}}
|
||||
|
||||
{{define "printtrace"}}{{if .PrintTrace}} print("SYSCALL: {{.Name}}(", {{.ParamPrintList}}") (", {{.Rets.PrintList}}")\n")
|
||||
{{end}}{{end}}
|
||||
|
||||
`
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
//
|
||||
package syscall
|
||||
|
||||
//go:generate go run mksyscall_windows.go -systemdll -output zsyscall_windows.go syscall_windows.go security_windows.go
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -systemdll -output zsyscall_windows.go syscall_windows.go security_windows.go
|
||||
|
||||
// StringByteSlice converts a string to a NUL-terminated []byte,
|
||||
// If s contains a NUL byte this function panics instead of
|
||||
|
||||
@@ -337,7 +337,6 @@ func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1)
|
||||
//sysnb ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_ioctl
|
||||
//sysnb execve(path *byte, argv **byte, envp **byte) (err error)
|
||||
//sysnb exit(res int) (err error)
|
||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error)
|
||||
//sys fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (val int, err error) = SYS_fcntl
|
||||
//sys unlinkat(fd int, path string, flags int) (err error)
|
||||
//sys openat(fd int, path string, flags int, perm uint32) (fdret int, err error)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user