mirror of https://github.com/golang/go.git
[dev.typeparams] all: merge master (16e82be) into dev.typeparams
Merge List: + 2021-06-2116e82be454runtime: fix crash during VDSO calls on PowerPC + 2021-06-212e542c3c06runtime/pprof: deflake TestMorestack more + 2021-06-21ced0fdbad0doc/go1.17: note deprecation of 'go get' for installing commands + 2021-06-217a5e7047a4doc/go1.17: add Go 1.18 pre-announcements + 2021-06-2185a2e24afddoc/go1.17: add security-related release notes + 2021-06-211de332996cdoc/go1.17: document go/parser.SkipObjectResolution + 2021-06-21117ebe0f52cmd/go: do not require the module cache to exist for 'go mod edit' + 2021-06-20460900a7b5os/signal: test with a significantly longer fatal timeout + 2021-06-19b73cc4b02bdatabase/sql: do not rely on timeout for deadlock test + 2021-06-1886743e7d86image: add RGBA64Image interface + 2021-06-189401172166runtime: clarify Frames.Next documentation + 2021-06-1857aaa19aaeruntime: disable CPU profiling before removing the SIGPROF handler + 2021-06-186f22d2c682doc/go1.17: fix typo + 2021-06-1745f251ad6ccmd/pprof,runtime/pprof: disable test on more broken platforms + 2021-06-17ed834853adcmd/go: replace a TODO with an explanatory comment + 2021-06-174dede02550cmd/pprof: make ObjAddr a no-op + 2021-06-1797cee43c93testing: drop unusual characters from TempDir directory name + 2021-06-17b0355a3e72time: fix receiver for Time.IsDST method + 2021-06-17881b6ea7badoc/go1.17: fix redundant space + 2021-06-160e67ce3d28cmd/go: in lazy modules, add transitive imports for 'go get' arguments + 2021-06-166ea2af0890cmd/go: add a regression test for #45979 + 2021-06-16a294e4e798math/rand: mention half-open intervals explicitly + 2021-06-16a6a853f94ccmd/asm: restore supporting of *1 scaling on ARM64 Change-Id: Ifdcb817fd44b4fa9c477042b41da55d1d769b016
This commit is contained in:
commit
d626ba27bb
|
|
@ -28,6 +28,38 @@ pkg encoding/csv, method (*Reader) FieldPos(int) (int, int)
|
||||||
pkg go/build, type Context struct, ToolTags []string
|
pkg go/build, type Context struct, ToolTags []string
|
||||||
pkg go/parser, const SkipObjectResolution = 64
|
pkg go/parser, const SkipObjectResolution = 64
|
||||||
pkg go/parser, const SkipObjectResolution Mode
|
pkg go/parser, const SkipObjectResolution Mode
|
||||||
|
pkg image, method (*Alpha) RGBA64At(int, int) color.RGBA64
|
||||||
|
pkg image, method (*Alpha) SetRGBA64(int, int, color.RGBA64)
|
||||||
|
pkg image, method (*Alpha16) RGBA64At(int, int) color.RGBA64
|
||||||
|
pkg image, method (*Alpha16) SetRGBA64(int, int, color.RGBA64)
|
||||||
|
pkg image, method (*CMYK) RGBA64At(int, int) color.RGBA64
|
||||||
|
pkg image, method (*CMYK) SetRGBA64(int, int, color.RGBA64)
|
||||||
|
pkg image, method (*Gray) RGBA64At(int, int) color.RGBA64
|
||||||
|
pkg image, method (*Gray) SetRGBA64(int, int, color.RGBA64)
|
||||||
|
pkg image, method (*Gray16) RGBA64At(int, int) color.RGBA64
|
||||||
|
pkg image, method (*Gray16) SetRGBA64(int, int, color.RGBA64)
|
||||||
|
pkg image, method (*NRGBA) RGBA64At(int, int) color.RGBA64
|
||||||
|
pkg image, method (*NRGBA) SetRGBA64(int, int, color.RGBA64)
|
||||||
|
pkg image, method (*NRGBA64) RGBA64At(int, int) color.RGBA64
|
||||||
|
pkg image, method (*NRGBA64) SetRGBA64(int, int, color.RGBA64)
|
||||||
|
pkg image, method (*NYCbCrA) RGBA64At(int, int) color.RGBA64
|
||||||
|
pkg image, method (*Paletted) RGBA64At(int, int) color.RGBA64
|
||||||
|
pkg image, method (*Paletted) SetRGBA64(int, int, color.RGBA64)
|
||||||
|
pkg image, method (*RGBA) RGBA64At(int, int) color.RGBA64
|
||||||
|
pkg image, method (*RGBA) SetRGBA64(int, int, color.RGBA64)
|
||||||
|
pkg image, method (*YCbCr) RGBA64At(int, int) color.RGBA64
|
||||||
|
pkg image, type RGBA64Image interface { At, Bounds, ColorModel, RGBA64At }
|
||||||
|
pkg image, type RGBA64Image interface, At(int, int) color.Color
|
||||||
|
pkg image, type RGBA64Image interface, Bounds() Rectangle
|
||||||
|
pkg image, type RGBA64Image interface, ColorModel() color.Model
|
||||||
|
pkg image, type RGBA64Image interface, RGBA64At(int, int) color.RGBA64
|
||||||
|
pkg image/draw, type RGBA64Image interface { At, Bounds, ColorModel, RGBA64At, Set, SetRGBA64 }
|
||||||
|
pkg image/draw, type RGBA64Image interface, At(int, int) color.Color
|
||||||
|
pkg image/draw, type RGBA64Image interface, Bounds() image.Rectangle
|
||||||
|
pkg image/draw, type RGBA64Image interface, ColorModel() color.Model
|
||||||
|
pkg image/draw, type RGBA64Image interface, RGBA64At(int, int) color.RGBA64
|
||||||
|
pkg image/draw, type RGBA64Image interface, Set(int, int, color.Color)
|
||||||
|
pkg image/draw, type RGBA64Image interface, SetRGBA64(int, int, color.RGBA64)
|
||||||
pkg io/fs, func FileInfoToDirEntry(FileInfo) DirEntry
|
pkg io/fs, func FileInfoToDirEntry(FileInfo) DirEntry
|
||||||
pkg math, const MaxFloat64 = 1.79769e+308 // 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368
|
pkg math, const MaxFloat64 = 1.79769e+308 // 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368
|
||||||
pkg math, const MaxInt = 9223372036854775807
|
pkg math, const MaxInt = 9223372036854775807
|
||||||
|
|
@ -153,7 +185,7 @@ pkg time, const Layout = "01/02 03:04:05PM '06 -0700"
|
||||||
pkg time, const Layout ideal-string
|
pkg time, const Layout ideal-string
|
||||||
pkg time, func UnixMicro(int64) Time
|
pkg time, func UnixMicro(int64) Time
|
||||||
pkg time, func UnixMilli(int64) Time
|
pkg time, func UnixMilli(int64) Time
|
||||||
pkg time, method (*Time) IsDST() bool
|
|
||||||
pkg time, method (Time) GoString() string
|
pkg time, method (Time) GoString() string
|
||||||
|
pkg time, method (Time) IsDST() bool
|
||||||
pkg time, method (Time) UnixMicro() int64
|
pkg time, method (Time) UnixMicro() int64
|
||||||
pkg time, method (Time) UnixMilli() int64
|
pkg time, method (Time) UnixMilli() int64
|
||||||
|
|
|
||||||
178
doc/go1.17.html
178
doc/go1.17.html
|
|
@ -162,7 +162,7 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
By default, <code>go</code> <code>mod</code> <code>tidy</code> verifies that
|
By default, <code>go</code> <code>mod</code> <code>tidy</code> verifies that
|
||||||
the selected versions of dependencies relevant to the main module are the same
|
the selected versions of dependencies relevant to the main module are the same
|
||||||
versions that would be used by the prior Go release (Go 1.16 for a module that
|
versions that would be used by the prior Go release (Go 1.16 for a module that
|
||||||
spsecifies <code>go</code> <code>1.17</code>), and preserves
|
specifies <code>go</code> <code>1.17</code>), and preserves
|
||||||
the <code>go.sum</code> entries needed by that release even for dependencies
|
the <code>go.sum</code> entries needed by that release even for dependencies
|
||||||
that are not normally needed by other commands.
|
that are not normally needed by other commands.
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -214,6 +214,16 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
<code>environment</code> for details.
|
<code>environment</code> for details.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p><!-- golang.org/issue/43684 -->
|
||||||
|
<code>go</code> <code>get</code> prints a deprecation warning when installing
|
||||||
|
commands outside the main module (without the <code>-d</code> flag).
|
||||||
|
<code>go</code> <code>install</code> <code>cmd@version</code> should be used
|
||||||
|
instead to install a command at a specific version, using a suffix like
|
||||||
|
<code>@latest</code> or <code>@v1.2.3</code>. In Go 1.18, the <code>-d</code>
|
||||||
|
flag will always be enabled, and <code>go</code> <code>get</code> will only
|
||||||
|
be used to change dependencies in <code>go.mod</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h4 id="missing-go-directive"><code>go.mod</code> files missing <code>go</code> directives</h4>
|
<h4 id="missing-go-directive"><code>go.mod</code> files missing <code>go</code> directives</h4>
|
||||||
|
|
||||||
<p><!-- golang.org/issue/44976 -->
|
<p><!-- golang.org/issue/44976 -->
|
||||||
|
|
@ -387,7 +397,7 @@ func Foo() bool {
|
||||||
registers instead of the stack. This work is enabled for Linux, MacOS, and
|
registers instead of the stack. This work is enabled for Linux, MacOS, and
|
||||||
Windows on the 64-bit x86 architecture (the <code>linux/amd64</code>,
|
Windows on the 64-bit x86 architecture (the <code>linux/amd64</code>,
|
||||||
<code>darwin/amd64</code>, <code>windows/amd64</code> ports). For a
|
<code>darwin/amd64</code>, <code>windows/amd64</code> ports). For a
|
||||||
representative set of Go packages and programs, benchmarking has shown
|
representative set of Go packages and programs, benchmarking has shown
|
||||||
performance improvements of about 5%, and a typical reduction in binary size
|
performance improvements of about 5%, and a typical reduction in binary size
|
||||||
of about 2%.
|
of about 2%.
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -441,6 +451,67 @@ func Foo() bool {
|
||||||
<a href="/pkg/runtime/cgo#Handle">runtime/cgo.Handle</a> for more information.
|
<a href="/pkg/runtime/cgo#Handle">runtime/cgo.Handle</a> for more information.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h3 id="semicolons">URL query parsing</h3>
|
||||||
|
<!-- CL 325697, CL 326309 -->
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The <code>net/url</code> and <code>net/http</code> packages used to accept
|
||||||
|
<code>";"</code> (semicolon) as a setting separator in URL queries, in
|
||||||
|
addition to <code>"&"</code> (ampersand). Now, settings with non-percent-encoded
|
||||||
|
semicolons are rejected and <code>net/http</code> servers will log a warning to
|
||||||
|
<a href="/pkg/net/http#Server.ErrorLog"><code>Server.ErrorLog</code></a>
|
||||||
|
when encountering one in a request URL.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
For example, before Go 1.17 the <a href="/pkg/net/url#URL.Query"><code>Query</code></a>
|
||||||
|
method of the URL <code>example?a=1;b=2&c=3</code> would have returned
|
||||||
|
<code>map[a:[1] b:[2] c:[3]]</code>, while now it returns <code>map[c:[3]]</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
When encountering such a query string,
|
||||||
|
<a href="/pkg/net/url#URL.Query"><code>URL.Query</code></a>
|
||||||
|
and
|
||||||
|
<a href="/pkg/net/http#Request.FormValue"><code>Request.FormValue</code></a>
|
||||||
|
ignore any settings that contain a semicolon,
|
||||||
|
<a href="/pkg/net/url#ParseQuery"><code>ParseQuery</code></a>
|
||||||
|
returns the remaining settings and an error, and
|
||||||
|
<a href="/pkg/net/http#Request.ParseForm"><code>Request.ParseForm</code></a>
|
||||||
|
and
|
||||||
|
<a href="/pkg/net/http#Request.ParseMultipartForm"><code>Request.ParseMultipartForm</code></a>
|
||||||
|
return an error but still set <code>Request</code> fields based on the
|
||||||
|
remaining settings.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<code>net/http</code> users can restore the original behavior by using the new
|
||||||
|
<a href="/pkg/net/http#AllowQuerySemicolons"><code>AllowQuerySemicolons</code></a>
|
||||||
|
handler wrapper. This will also suppress the <code>ErrorLog</code> warning.
|
||||||
|
Note that accepting semicolons as query separators can lead to security issues
|
||||||
|
if different systems interpret cache keys differently.
|
||||||
|
See <a href="https://golang.org/issue/25192">issue 25192</a> for more information.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="ALPN">TLS strict ALPN</h3>
|
||||||
|
<!-- CL 289209, CL 325432 -->
|
||||||
|
|
||||||
|
<p>
|
||||||
|
When <a href="/pkg/crypto/tls#Config.NextProtos"><code>Config.NextProtos</code></a>
|
||||||
|
is set, servers now enforce that there is an overlap between the configured
|
||||||
|
protocols and the ALPN protocols advertised by the client, if any. If there is
|
||||||
|
no mutually supported protocol, the connection is closed with the
|
||||||
|
<code>no_application_protocol</code> alert, as required by RFC 7301. This
|
||||||
|
helps mitigate <a href="https://alpaca-attack.com/">the ALPACA cross-protocol attack</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
As an exception, when the value <code>"h2"</code> is included in the server's
|
||||||
|
<code>Config.NextProtos</code>, HTTP/1.1 clients will be allowed to connect as
|
||||||
|
if they didn't support ALPN.
|
||||||
|
See <a href="https://golang.org/issue/46310">issue 46310</a> for more information.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h3 id="minor_library_changes">Minor changes to the library</h3>
|
<h3 id="minor_library_changes">Minor changes to the library</h3>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -549,14 +620,6 @@ func Foo() bool {
|
||||||
methods. Canceling the context after the handshake has finished has no effect.
|
methods. Canceling the context after the handshake has finished has no effect.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p><!-- CL 289209 -->
|
|
||||||
When <a href="/pkg/crypto/tls#Config.NextProtos"><code>Config.NextProtos</code></a>
|
|
||||||
is set, servers now enforce that there is an overlap between the
|
|
||||||
configured protocols and the protocols advertised by the client, if any.
|
|
||||||
If there is no overlap the connection is closed with the
|
|
||||||
<code>no_application_protocol</code> alert, as required by RFC 7301.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p><!-- CL 314609 -->
|
<p><!-- CL 314609 -->
|
||||||
Cipher suite ordering is now handled entirely by the
|
Cipher suite ordering is now handled entirely by the
|
||||||
<code>crypto/tls</code> package. Currently, cipher suites are sorted based
|
<code>crypto/tls</code> package. Currently, cipher suites are sorted based
|
||||||
|
|
@ -576,6 +639,15 @@ func Foo() bool {
|
||||||
weakness</a>. They are still enabled by default but only as a last resort,
|
weakness</a>. They are still enabled by default but only as a last resort,
|
||||||
thanks to the cipher suite ordering change above.
|
thanks to the cipher suite ordering change above.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p><!-- golang.org/issue/45428 -->
|
||||||
|
Beginning in the next release, Go 1.18, the
|
||||||
|
<a href="/pkg/crypto/tls/#Config.MinVersion"><code>Config.MinVersion</code></a>
|
||||||
|
for <code>crypto/tls</code> clients will default to TLS 1.2, disabling TLS 1.0
|
||||||
|
and TLS 1.1 by default. Applications will be able to override the change by
|
||||||
|
explicitly setting <code>Config.MinVersion</code>.
|
||||||
|
This will not affect <code>crypto/tls</code> servers.
|
||||||
|
</p>
|
||||||
</dd>
|
</dd>
|
||||||
</dl><!-- crypto/tls -->
|
</dl><!-- crypto/tls -->
|
||||||
|
|
||||||
|
|
@ -603,6 +675,14 @@ func Foo() bool {
|
||||||
roots. This adds support for the new system trusted certificate store in
|
roots. This adds support for the new system trusted certificate store in
|
||||||
FreeBSD 12.2+.
|
FreeBSD 12.2+.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p><!-- golang.org/issue/41682 -->
|
||||||
|
Beginning in the next release, Go 1.18, <code>crypto/x509</code> will
|
||||||
|
reject certificates signed with the SHA-1 hash function. This doesn't
|
||||||
|
apply to self-signed root certificates. Practical attacks against SHA-1
|
||||||
|
<a href="https://shattered.io/">have been demonstrated in 2017</a> and publicly
|
||||||
|
trusted Certificate Authorities have not issued SHA-1 certificates since 2015.
|
||||||
|
</p>
|
||||||
</dd>
|
</dd>
|
||||||
</dl><!-- crypto/x509 -->
|
</dl><!-- crypto/x509 -->
|
||||||
|
|
||||||
|
|
@ -658,6 +738,22 @@ func Foo() bool {
|
||||||
</dd>
|
</dd>
|
||||||
</dl><!-- encoding/csv -->
|
</dl><!-- encoding/csv -->
|
||||||
|
|
||||||
|
<dl id="encoding/xml"><dt><a href="/pkg/encoding/xml/">encoding/xml</a></dt>
|
||||||
|
<dd>
|
||||||
|
<p><!-- CL 277893 -->
|
||||||
|
When a comment appears within a
|
||||||
|
<a href="/pkg/encoding/xml/#Directive"><code>Directive</code></a>, it is now replaced
|
||||||
|
with a single space instead of being completely elided.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Invalid element or attribute names with leading, trailing, or multiple
|
||||||
|
colons are now stored unmodified into the
|
||||||
|
<a href="/pkg/encoding/xml/#Name"><code>Name.Local</code></a> field.
|
||||||
|
</p>
|
||||||
|
</dd>
|
||||||
|
</dl><!-- encoding/xml -->
|
||||||
|
|
||||||
<dl id="flag"><dt><a href="/pkg/flag/">flag</a></dt>
|
<dl id="flag"><dt><a href="/pkg/flag/">flag</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<p><!-- CL 271788 -->
|
<p><!-- CL 271788 -->
|
||||||
|
|
@ -693,6 +789,29 @@ func Foo() bool {
|
||||||
</dd>
|
</dd>
|
||||||
</dl><!-- go/format -->
|
</dl><!-- go/format -->
|
||||||
|
|
||||||
|
<dl id="go/parser"><dt><a href="/pkg/go/parser/">go/parser</a></dt>
|
||||||
|
<dd>
|
||||||
|
<p><!-- CL 306149 -->
|
||||||
|
The new <a href="/pkg/go/parser/#SkipObjectResolution"><code>SkipObjectResolution</code></a>
|
||||||
|
<code>Mode</code> value instructs the parser not to resolve identifiers to
|
||||||
|
their declaration. This may improve parsing speed.
|
||||||
|
</p>
|
||||||
|
</dd>
|
||||||
|
</dl><!-- go/parser -->
|
||||||
|
|
||||||
|
<dl id="image"><dt><a href="/pkg/image/">image</a></dt>
|
||||||
|
<dd>
|
||||||
|
<p><!-- CL 311129 -->
|
||||||
|
The concrete image types (<code>RGBA</code>, <code>Gray16</code> and so on)
|
||||||
|
now implement a new <a href="/pkg/image/#RGBA64Image"><code>RGBA64Image</code></a>
|
||||||
|
interface. Those concrete types, other than the chroma-subsampling
|
||||||
|
related <code>YCbCr</code> and <code>NYCbCrA</code>, also now implement
|
||||||
|
<a href="/pkg/image/draw/#RGBA64Image"><code>draw.RGBA64Image</code></a>, a
|
||||||
|
new interface in the <code>image/draw</code> package.
|
||||||
|
</p>
|
||||||
|
</dd>
|
||||||
|
</dl><!-- image -->
|
||||||
|
|
||||||
<dl id="io/fs"><dt><a href="/pkg/io/fs/">io/fs</a></dt>
|
<dl id="io/fs"><dt><a href="/pkg/io/fs/">io/fs</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<p><!-- CL 293649 -->
|
<p><!-- CL 293649 -->
|
||||||
|
|
@ -721,6 +840,20 @@ func Foo() bool {
|
||||||
</dd>
|
</dd>
|
||||||
</dl><!-- mime -->
|
</dl><!-- mime -->
|
||||||
|
|
||||||
|
<dl id="mime/multipart"><dt><a href="/pkg/mime/multipart/">mime/multipart</a></dt>
|
||||||
|
<dd>
|
||||||
|
<p><!-- CL 313809 -->
|
||||||
|
<a href="/pkg/mime/multipart/#Part.FileName"><code>Part.FileName</code></a>
|
||||||
|
now applies
|
||||||
|
<a href="/pkg/path/filepath/#Base"><code>filepath.Base</code></a> to the
|
||||||
|
return value. This mitigates potential path traversal vulnerabilities in
|
||||||
|
applications that accept multipart messages, such as <code>net/http</code>
|
||||||
|
servers that call
|
||||||
|
<a href="/pkg/net/http/#Request.FormFile"><code>Request.FormFile</code></a>.
|
||||||
|
</p>
|
||||||
|
</dd>
|
||||||
|
</dl><!-- mime/multipart -->
|
||||||
|
|
||||||
<dl id="net"><dt><a href="/pkg/net/">net</a></dt>
|
<dl id="net"><dt><a href="/pkg/net/">net</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<p><!-- CL 272668 -->
|
<p><!-- CL 272668 -->
|
||||||
|
|
@ -740,7 +873,7 @@ func Foo() bool {
|
||||||
the <a href="/pkg/net/#Error"><code>net.Error</code></a> interface.
|
the <a href="/pkg/net/#Error"><code>net.Error</code></a> interface.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p><!-- CL325829 -->
|
<p><!-- CL 325829 -->
|
||||||
The <a href="/pkg/net/#ParseIP"><code>ParseIP</code></a> and <a href="/pkg/net/#ParseCIDR"><code>ParseCIDR</code></a>
|
The <a href="/pkg/net/#ParseIP"><code>ParseIP</code></a> and <a href="/pkg/net/#ParseCIDR"><code>ParseCIDR</code></a>
|
||||||
functions now reject IPv4 addresses which contain decimal components with leading zeros.
|
functions now reject IPv4 addresses which contain decimal components with leading zeros.
|
||||||
|
|
||||||
|
|
@ -771,6 +904,29 @@ func Foo() bool {
|
||||||
The <a href="/pkg/net/http/#ReadRequest"><code>ReadRequest</code></a> function
|
The <a href="/pkg/net/http/#ReadRequest"><code>ReadRequest</code></a> function
|
||||||
now returns an error when the request has multiple Host headers.
|
now returns an error when the request has multiple Host headers.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p><!-- CL 313950 -->
|
||||||
|
When producing a redirect to the cleaned version of a URL,
|
||||||
|
<a href="/pkg/net/http/#ServeMux"><code>ServeMux</code></a> now always
|
||||||
|
uses relative URLs in the <code>Location</code> header. Previously it
|
||||||
|
would echo the full URL of the request, which could lead to unintended
|
||||||
|
redirects if the client could be made to send an absolute request URL.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><!-- CL 308009, CL 313489 -->
|
||||||
|
When interpreting certain HTTP headers handled by <code>net/http</code>,
|
||||||
|
non-ASCII characters are now ignored or rejected.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><!-- CL 325697 -->
|
||||||
|
If
|
||||||
|
<a href="/pkg/net/http/#Request.ParseForm"><code>Request.ParseForm</code></a>
|
||||||
|
returns an error when called by
|
||||||
|
<a href="/pkg/net/http/#Request.ParseMultipartForm"><code>Request.ParseMultipartForm</code></a>,
|
||||||
|
the latter now continues populating
|
||||||
|
<a href="/pkg/net/http/#Request.MultipartForm"><code>Request.MultipartForm</code></a>
|
||||||
|
before returning it.
|
||||||
|
</p>
|
||||||
</dd>
|
</dd>
|
||||||
</dl><!-- net/http -->
|
</dl><!-- net/http -->
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1003,7 +1003,8 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) {
|
||||||
p.errorf("unimplemented two-register form")
|
p.errorf("unimplemented two-register form")
|
||||||
}
|
}
|
||||||
a.Index = r1
|
a.Index = r1
|
||||||
if scale != 0 && p.arch.Family == sys.ARM64 {
|
if scale != 0 && scale != 1 && p.arch.Family == sys.ARM64 {
|
||||||
|
// Support (R1)(R2) (no scaling) and (R1)(R2*1).
|
||||||
p.errorf("arm64 doesn't support scaled register format")
|
p.errorf("arm64 doesn't support scaled register format")
|
||||||
} else {
|
} else {
|
||||||
a.Scale = int16(scale)
|
a.Scale = int16(scale)
|
||||||
|
|
|
||||||
|
|
@ -547,6 +547,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
|
||||||
// shifted or extended register offset.
|
// shifted or extended register offset.
|
||||||
MOVD (R2)(R6.SXTW), R4 // 44c866f8
|
MOVD (R2)(R6.SXTW), R4 // 44c866f8
|
||||||
MOVD (R3)(R6), R5 // 656866f8
|
MOVD (R3)(R6), R5 // 656866f8
|
||||||
|
MOVD (R3)(R6*1), R5 // 656866f8
|
||||||
MOVD (R2)(R6), R4 // 446866f8
|
MOVD (R2)(R6), R4 // 446866f8
|
||||||
MOVWU (R19)(R20<<2), R20 // 747a74b8
|
MOVWU (R19)(R20<<2), R20 // 747a74b8
|
||||||
MOVD (R2)(R6<<3), R4 // 447866f8
|
MOVD (R2)(R6<<3), R4 // 447866f8
|
||||||
|
|
@ -579,6 +580,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
|
||||||
MOVB R4, (R2)(R6.SXTX) // 44e82638
|
MOVB R4, (R2)(R6.SXTX) // 44e82638
|
||||||
MOVB R8, (R3)(R9.UXTW) // 68482938
|
MOVB R8, (R3)(R9.UXTW) // 68482938
|
||||||
MOVB R10, (R5)(R8) // aa682838
|
MOVB R10, (R5)(R8) // aa682838
|
||||||
|
MOVB R10, (R5)(R8*1) // aa682838
|
||||||
MOVH R11, (R2)(R7.SXTW<<1) // 4bd82778
|
MOVH R11, (R2)(R7.SXTW<<1) // 4bd82778
|
||||||
MOVH R5, (R1)(R2<<1) // 25782278
|
MOVH R5, (R1)(R2<<1) // 25782278
|
||||||
MOVH R7, (R2)(R5.SXTX<<1) // 47f82578
|
MOVH R7, (R2)(R5.SXTX<<1) // 47f82578
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ func lockVersion(mod module.Version) (unlock func(), err error) {
|
||||||
// If err is nil, the caller MUST eventually call the unlock function.
|
// If err is nil, the caller MUST eventually call the unlock function.
|
||||||
func SideLock() (unlock func(), err error) {
|
func SideLock() (unlock func(), err error) {
|
||||||
if err := checkCacheDir(); err != nil {
|
if err := checkCacheDir(); err != nil {
|
||||||
base.Fatalf("go: %v", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
path := filepath.Join(cfg.GOMODCACHE, "cache", "lock")
|
path := filepath.Join(cfg.GOMODCACHE, "cache", "lock")
|
||||||
|
|
|
||||||
|
|
@ -1153,6 +1153,7 @@ func (r *resolver) loadPackages(ctx context.Context, patterns []string, findPack
|
||||||
Tags: imports.AnyTags(),
|
Tags: imports.AnyTags(),
|
||||||
VendorModulesInGOROOTSrc: true,
|
VendorModulesInGOROOTSrc: true,
|
||||||
LoadTests: *getT,
|
LoadTests: *getT,
|
||||||
|
AssumeRootsImported: true, // After 'go get foo', imports of foo should build.
|
||||||
SilencePackageErrors: true, // May be fixed by subsequent upgrades or downgrades.
|
SilencePackageErrors: true, // May be fixed by subsequent upgrades or downgrades.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -443,7 +443,7 @@ func expandGraph(ctx context.Context, rs *Requirements) (*Requirements, *ModuleG
|
||||||
// roots — but in a lazy module it may pull in previously-irrelevant
|
// roots — but in a lazy module it may pull in previously-irrelevant
|
||||||
// transitive dependencies.
|
// transitive dependencies.
|
||||||
|
|
||||||
newRS, rsErr := updateRoots(ctx, rs.direct, rs, nil, nil)
|
newRS, rsErr := updateRoots(ctx, rs.direct, rs, nil, nil, false)
|
||||||
if rsErr != nil {
|
if rsErr != nil {
|
||||||
// Failed to update roots, perhaps because of an error in a transitive
|
// Failed to update roots, perhaps because of an error in a transitive
|
||||||
// dependency needed for the update. Return the original Requirements
|
// dependency needed for the update. Return the original Requirements
|
||||||
|
|
@ -517,11 +517,11 @@ func tidyRoots(ctx context.Context, rs *Requirements, pkgs []*loadPkg) (*Require
|
||||||
return tidyLazyRoots(ctx, rs.direct, pkgs)
|
return tidyLazyRoots(ctx, rs.direct, pkgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version) (*Requirements, error) {
|
func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) {
|
||||||
if rs.depth == eager {
|
if rs.depth == eager {
|
||||||
return updateEagerRoots(ctx, direct, rs, add)
|
return updateEagerRoots(ctx, direct, rs, add)
|
||||||
}
|
}
|
||||||
return updateLazyRoots(ctx, direct, rs, pkgs, add)
|
return updateLazyRoots(ctx, direct, rs, pkgs, add, rootsImported)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tidyLazyRoots returns a minimal set of root requirements that maintains the
|
// tidyLazyRoots returns a minimal set of root requirements that maintains the
|
||||||
|
|
@ -661,7 +661,7 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg)
|
||||||
//
|
//
|
||||||
// (See https://golang.org/design/36460-lazy-module-loading#invariants for more
|
// (See https://golang.org/design/36460-lazy-module-loading#invariants for more
|
||||||
// detail.)
|
// detail.)
|
||||||
func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version) (*Requirements, error) {
|
func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) {
|
||||||
roots := rs.rootModules
|
roots := rs.rootModules
|
||||||
rootsUpgraded := false
|
rootsUpgraded := false
|
||||||
|
|
||||||
|
|
@ -688,6 +688,10 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen
|
||||||
//
|
//
|
||||||
// (This is the “import invariant” that makes lazy loading possible.)
|
// (This is the “import invariant” that makes lazy loading possible.)
|
||||||
|
|
||||||
|
case rootsImported && pkg.flags.has(pkgFromRoot):
|
||||||
|
// pkg is a transitive dependency of some root, and we are treating the
|
||||||
|
// roots as if they are imported by the main module (as in 'go get').
|
||||||
|
|
||||||
case pkg.flags.has(pkgIsRoot):
|
case pkg.flags.has(pkgIsRoot):
|
||||||
// pkg is a root of the package-import graph. (Generally this means that
|
// pkg is a root of the package-import graph. (Generally this means that
|
||||||
// it matches a command-line argument.) We want future invocations of the
|
// it matches a command-line argument.) We want future invocations of the
|
||||||
|
|
|
||||||
|
|
@ -661,7 +661,7 @@ func requirementsFromModFile(ctx context.Context) *Requirements {
|
||||||
for _, n := range mPathCount {
|
for _, n := range mPathCount {
|
||||||
if n > 1 {
|
if n > 1 {
|
||||||
var err error
|
var err error
|
||||||
rs, err = updateRoots(ctx, rs.direct, rs, nil, nil)
|
rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
base.Fatalf("go: %v", err)
|
base.Fatalf("go: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,11 @@ type PackageOpts struct {
|
||||||
// if the flag is set to "readonly" (the default) or "vendor".
|
// if the flag is set to "readonly" (the default) or "vendor".
|
||||||
ResolveMissingImports bool
|
ResolveMissingImports bool
|
||||||
|
|
||||||
|
// AssumeRootsImported indicates that the transitive dependencies of the root
|
||||||
|
// packages should be treated as if those roots will be imported by the main
|
||||||
|
// module.
|
||||||
|
AssumeRootsImported bool
|
||||||
|
|
||||||
// AllowPackage, if non-nil, is called after identifying the module providing
|
// AllowPackage, if non-nil, is called after identifying the module providing
|
||||||
// each package. If AllowPackage returns a non-nil error, that error is set
|
// each package. If AllowPackage returns a non-nil error, that error is set
|
||||||
// for the package, and the imports and test of that package will not be
|
// for the package, and the imports and test of that package will not be
|
||||||
|
|
@ -875,6 +880,11 @@ const (
|
||||||
// are also roots (and must be marked pkgIsRoot).
|
// are also roots (and must be marked pkgIsRoot).
|
||||||
pkgIsRoot
|
pkgIsRoot
|
||||||
|
|
||||||
|
// pkgFromRoot indicates that the package is in the transitive closure of
|
||||||
|
// imports starting at the roots. (Note that every package marked as pkgIsRoot
|
||||||
|
// is also trivially marked pkgFromRoot.)
|
||||||
|
pkgFromRoot
|
||||||
|
|
||||||
// pkgImportsLoaded indicates that the imports and testImports fields of a
|
// pkgImportsLoaded indicates that the imports and testImports fields of a
|
||||||
// loadPkg have been populated.
|
// loadPkg have been populated.
|
||||||
pkgImportsLoaded
|
pkgImportsLoaded
|
||||||
|
|
@ -1068,7 +1078,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
|
||||||
// iteration so we don't need to also update it here. (That would waste time
|
// iteration so we don't need to also update it here. (That would waste time
|
||||||
// computing a "direct" map that we'll have to recompute later anyway.)
|
// computing a "direct" map that we'll have to recompute later anyway.)
|
||||||
direct := ld.requirements.direct
|
direct := ld.requirements.direct
|
||||||
rs, err := updateRoots(ctx, direct, ld.requirements, noPkgs, toAdd)
|
rs, err := updateRoots(ctx, direct, ld.requirements, noPkgs, toAdd, ld.AssumeRootsImported)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If an error was found in a newly added module, report the package
|
// If an error was found in a newly added module, report the package
|
||||||
// import stack instead of the module requirement stack. Packages
|
// import stack instead of the module requirement stack. Packages
|
||||||
|
|
@ -1274,7 +1284,7 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err
|
||||||
addRoots = tidy.rootModules
|
addRoots = tidy.rootModules
|
||||||
}
|
}
|
||||||
|
|
||||||
rs, err = updateRoots(ctx, direct, rs, ld.pkgs, addRoots)
|
rs, err = updateRoots(ctx, direct, rs, ld.pkgs, addRoots, ld.AssumeRootsImported)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// We don't actually know what even the root requirements are supposed to be,
|
// We don't actually know what even the root requirements are supposed to be,
|
||||||
// so we can't proceed with loading. Return the error to the caller
|
// so we can't proceed with loading. Return the error to the caller
|
||||||
|
|
@ -1433,6 +1443,9 @@ func (ld *loader) applyPkgFlags(ctx context.Context, pkg *loadPkg, flags loadPkg
|
||||||
// This package matches a root pattern by virtue of being in "all".
|
// This package matches a root pattern by virtue of being in "all".
|
||||||
flags |= pkgIsRoot
|
flags |= pkgIsRoot
|
||||||
}
|
}
|
||||||
|
if flags.has(pkgIsRoot) {
|
||||||
|
flags |= pkgFromRoot
|
||||||
|
}
|
||||||
|
|
||||||
old := pkg.flags.update(flags)
|
old := pkg.flags.update(flags)
|
||||||
new := old | flags
|
new := old | flags
|
||||||
|
|
@ -1487,6 +1500,12 @@ func (ld *loader) applyPkgFlags(ctx context.Context, pkg *loadPkg, flags loadPkg
|
||||||
ld.applyPkgFlags(ctx, dep, pkgInAll)
|
ld.applyPkgFlags(ctx, dep, pkgInAll)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if new.has(pkgFromRoot) && !old.has(pkgFromRoot|pkgImportsLoaded) {
|
||||||
|
for _, dep := range pkg.imports {
|
||||||
|
ld.applyPkgFlags(ctx, dep, pkgFromRoot)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// preloadRootModules loads the module requirements needed to identify the
|
// preloadRootModules loads the module requirements needed to identify the
|
||||||
|
|
@ -1549,7 +1568,7 @@ func (ld *loader) preloadRootModules(ctx context.Context, rootPkgs []string) (ch
|
||||||
}
|
}
|
||||||
module.Sort(toAdd)
|
module.Sort(toAdd)
|
||||||
|
|
||||||
rs, err := updateRoots(ctx, ld.requirements.direct, ld.requirements, nil, toAdd)
|
rs, err := updateRoots(ctx, ld.requirements.direct, ld.requirements, nil, toAdd, ld.AssumeRootsImported)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// We are missing some root dependency, and for some reason we can't load
|
// We are missing some root dependency, and for some reason we can't load
|
||||||
// enough of the module dependency graph to add the missing root. Package
|
// enough of the module dependency graph to add the missing root. Package
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 'go mod edit' opportunistically locks the side-lock file in the module cache,
|
||||||
|
# for compatibility with older versions of the 'go' command.
|
||||||
|
# It does not otherwise depend on the module cache, so it should not
|
||||||
|
# fail if the module cache directory cannot be created.
|
||||||
|
|
||||||
|
[root] skip
|
||||||
|
|
||||||
|
mkdir $WORK/readonly
|
||||||
|
chmod 0555 $WORK/readonly
|
||||||
|
env GOPATH=$WORK/readonly/nonexist
|
||||||
|
|
||||||
|
go mod edit -go=1.17
|
||||||
|
|
||||||
|
-- go.mod --
|
||||||
|
module example.com/m
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
# https://golang.org/issue/45979: after 'go get' on a package,
|
||||||
|
# that package should be importable without error.
|
||||||
|
|
||||||
|
|
||||||
|
# We start out with an unresolved dependency.
|
||||||
|
# 'go list' suggests that we run 'go get' on that dependency.
|
||||||
|
|
||||||
|
! go list -deps .
|
||||||
|
stderr '^m.go:3:8: no required module provides package rsc\.io/quote; to add it:\n\tgo get rsc.io/quote$'
|
||||||
|
|
||||||
|
|
||||||
|
# When we run the suggested 'go get' command, the new dependency can be used
|
||||||
|
# immediately.
|
||||||
|
#
|
||||||
|
# 'go get' marks the new dependency as 'indirect', because it doesn't scan
|
||||||
|
# enough source code to know whether it is direct, and it is easier and less
|
||||||
|
# invasive to remove an incorrect indirect mark (e.g. using 'go get') than to
|
||||||
|
# add one that is missing ('go mod tidy' or 'go mod vendor').
|
||||||
|
|
||||||
|
go get rsc.io/quote
|
||||||
|
grep 'rsc.io/quote v\d+\.\d+\.\d+ // indirect$' go.mod
|
||||||
|
! grep 'rsc.io/quote v\d+\.\d+\.\d+$' go.mod
|
||||||
|
|
||||||
|
go list -deps .
|
||||||
|
! stderr .
|
||||||
|
[!short] go build .
|
||||||
|
[!short] ! stderr .
|
||||||
|
|
||||||
|
|
||||||
|
# 'go get .' (or 'go mod tidy') removes the indirect mark.
|
||||||
|
|
||||||
|
go get .
|
||||||
|
grep 'rsc.io/quote v\d+\.\d+\.\d+$' go.mod
|
||||||
|
! grep 'rsc.io/quote v\d+\.\d+\.\d+ // indirect$' go.mod
|
||||||
|
|
||||||
|
|
||||||
|
-- go.mod --
|
||||||
|
module example.com/m
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
-- m.go --
|
||||||
|
package m
|
||||||
|
|
||||||
|
import _ "rsc.io/quote"
|
||||||
|
|
@ -10,45 +10,73 @@ go env GOSUMDB
|
||||||
stdout '^sum.golang.org$'
|
stdout '^sum.golang.org$'
|
||||||
|
|
||||||
# Download direct from github.
|
# Download direct from github.
|
||||||
|
|
||||||
[!net] skip
|
[!net] skip
|
||||||
[!exec:git] skip
|
[!exec:git] skip
|
||||||
env GOSUMDB=sum.golang.org
|
env GOSUMDB=sum.golang.org
|
||||||
env GOPROXY=direct
|
env GOPROXY=direct
|
||||||
|
|
||||||
go get -d rsc.io/quote@v1.5.2
|
go get -d rsc.io/quote@v1.5.2
|
||||||
cp go.sum saved.sum
|
cp go.sum saved.sum
|
||||||
|
|
||||||
|
|
||||||
# Download from proxy.golang.org with go.sum entry already.
|
# Download from proxy.golang.org with go.sum entry already.
|
||||||
# Use 'go list' instead of 'go get' since the latter may download extra go.mod
|
# Use 'go list' instead of 'go get' since the latter may download extra go.mod
|
||||||
# files not listed in go.sum.
|
# files not listed in go.sum.
|
||||||
|
|
||||||
go clean -modcache
|
go clean -modcache
|
||||||
env GOSUMDB=
|
env GOSUMDB=
|
||||||
env GOPROXY=
|
env GOPROXY=
|
||||||
go list -x -deps rsc.io/quote
|
|
||||||
|
go list -x -m all # Download go.mod files.
|
||||||
! stderr github
|
! stderr github
|
||||||
stderr proxy.golang.org/rsc.io/quote
|
stderr proxy.golang.org/rsc.io/quote
|
||||||
! stderr sum.golang.org/tile
|
! stderr sum.golang.org/tile
|
||||||
! stderr sum.golang.org/lookup/rsc.io/quote
|
! stderr sum.golang.org/lookup/rsc.io/quote
|
||||||
|
|
||||||
|
go list -x -deps rsc.io/quote # Download module source.
|
||||||
|
! stderr github
|
||||||
|
stderr proxy.golang.org/rsc.io/quote
|
||||||
|
! stderr sum.golang.org/tile
|
||||||
|
! stderr sum.golang.org/lookup/rsc.io/quote
|
||||||
|
|
||||||
cmp go.sum saved.sum
|
cmp go.sum saved.sum
|
||||||
|
|
||||||
|
|
||||||
# Download again.
|
# Download again.
|
||||||
# Should use the checksum database to validate new go.sum lines,
|
# Should use the checksum database to validate new go.sum lines,
|
||||||
# but not need to fetch any new data from the proxy.
|
# but not need to fetch any new data from the proxy.
|
||||||
|
|
||||||
rm go.sum
|
rm go.sum
|
||||||
go list -mod=mod -x rsc.io/quote
|
|
||||||
|
go list -mod=mod -x -m all # Add checksums for go.mod files.
|
||||||
|
stderr sum.golang.org/tile
|
||||||
! stderr github
|
! stderr github
|
||||||
! stderr proxy.golang.org/rsc.io/quote
|
! stderr proxy.golang.org/rsc.io/quote
|
||||||
stderr sum.golang.org/tile
|
|
||||||
stderr sum.golang.org/lookup/rsc.io/quote
|
stderr sum.golang.org/lookup/rsc.io/quote
|
||||||
|
|
||||||
|
go list -mod=mod -x rsc.io/quote # Add checksums for module source.
|
||||||
|
! stderr . # Adds checksums, but for entities already in the module cache.
|
||||||
|
|
||||||
cmp go.sum saved.sum
|
cmp go.sum saved.sum
|
||||||
|
|
||||||
|
|
||||||
# test fallback to direct
|
# test fallback to direct
|
||||||
|
|
||||||
env TESTGOPROXY404=1
|
env TESTGOPROXY404=1
|
||||||
go clean -modcache
|
go clean -modcache
|
||||||
rm go.sum
|
rm go.sum
|
||||||
go list -mod=mod -x rsc.io/quote
|
|
||||||
|
go list -mod=mod -x -m all # Download go.mod files
|
||||||
stderr 'proxy.golang.org.*404 testing'
|
stderr 'proxy.golang.org.*404 testing'
|
||||||
stderr github.com/rsc
|
stderr github.com/rsc
|
||||||
|
|
||||||
|
go list -mod=mod -x rsc.io/quote # Download module source.
|
||||||
|
stderr 'proxy.golang.org.*404 testing'
|
||||||
|
stderr github.com/rsc
|
||||||
|
|
||||||
cmp go.sum saved.sum
|
cmp go.sum saved.sum
|
||||||
|
|
||||||
|
|
||||||
-- go.mod --
|
-- go.mod --
|
||||||
module m
|
module m
|
||||||
|
|
|
||||||
|
|
@ -233,8 +233,8 @@ func (f *file) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *file) ObjAddr(addr uint64) (uint64, error) {
|
func (f *file) ObjAddr(addr uint64) (uint64, error) {
|
||||||
// No support for shared libraries.
|
// No support for shared libraries, so translation is a no-op.
|
||||||
return 0, nil
|
return addr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *file) BuildID() string {
|
func (f *file) BuildID() string {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"internal/testenv"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tmp, pprofExe string // populated by buildPprof
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
if !testenv.HasGoBuild() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var exitcode int
|
||||||
|
if err := buildPprof(); err == nil {
|
||||||
|
exitcode = m.Run()
|
||||||
|
} else {
|
||||||
|
fmt.Println(err)
|
||||||
|
exitcode = 1
|
||||||
|
}
|
||||||
|
os.RemoveAll(tmp)
|
||||||
|
os.Exit(exitcode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildPprof() error {
|
||||||
|
var err error
|
||||||
|
tmp, err = os.MkdirTemp("", "TestPprof")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("TempDir failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pprofExe = filepath.Join(tmp, "testpprof.exe")
|
||||||
|
gotool, err := testenv.GoTool()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
out, err := exec.Command(gotool, "build", "-o", pprofExe, "cmd/pprof").CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
os.RemoveAll(tmp)
|
||||||
|
return fmt.Errorf("go build -o %v cmd/pprof: %v\n%s", pprofExe, err, string(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// See also runtime/pprof.cpuProfilingBroken.
|
||||||
|
func mustHaveCPUProfiling(t *testing.T) {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "plan9":
|
||||||
|
t.Skipf("skipping on %s, unimplemented", runtime.GOOS)
|
||||||
|
case "aix":
|
||||||
|
t.Skipf("skipping on %s, issue 45170", runtime.GOOS)
|
||||||
|
case "ios", "dragonfly", "netbsd", "illumos", "solaris":
|
||||||
|
t.Skipf("skipping on %s, issue 13841", runtime.GOOS)
|
||||||
|
case "openbsd":
|
||||||
|
if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
|
||||||
|
t.Skipf("skipping on %s/%s, issue 13841", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustHaveDisasm(t *testing.T) {
|
||||||
|
switch runtime.GOARCH {
|
||||||
|
case "mips", "mipsle", "mips64", "mips64le":
|
||||||
|
t.Skipf("skipping on %s, issue 12559", runtime.GOARCH)
|
||||||
|
case "riscv64":
|
||||||
|
t.Skipf("skipping on %s, issue 36738", runtime.GOARCH)
|
||||||
|
case "s390x":
|
||||||
|
t.Skipf("skipping on %s, issue 15255", runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip PIE platforms, pprof can't disassemble PIE.
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skipf("skipping on %s, issue 46639", runtime.GOOS)
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
|
||||||
|
t.Skipf("skipping on %s/%s, issue 46639", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDisasm verifies that cmd/pprof can successfully disassemble functions.
|
||||||
|
//
|
||||||
|
// This is a regression test for issue 46636.
|
||||||
|
func TestDisasm(t *testing.T) {
|
||||||
|
mustHaveCPUProfiling(t)
|
||||||
|
mustHaveDisasm(t)
|
||||||
|
testenv.MustHaveGoBuild(t)
|
||||||
|
|
||||||
|
tmpdir := t.TempDir()
|
||||||
|
cpuExe := filepath.Join(tmpdir, "cpu.exe")
|
||||||
|
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", cpuExe, "cpu.go")
|
||||||
|
cmd.Dir = "testdata/"
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("build failed: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
profile := filepath.Join(tmpdir, "cpu.pprof")
|
||||||
|
cmd = exec.Command(cpuExe, "-output", profile)
|
||||||
|
out, err = cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cpu failed: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = exec.Command(pprofExe, "-disasm", "main.main", cpuExe, profile)
|
||||||
|
out, err = cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("pprof failed: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
sout := string(out)
|
||||||
|
want := "ROUTINE ======================== main.main"
|
||||||
|
if !strings.Contains(sout, want) {
|
||||||
|
t.Errorf("pprof disasm got %s want contains %q", sout, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime/pprof"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var output = flag.String("output", "", "pprof profile output file")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
if *output == "" {
|
||||||
|
fmt.Fprintf(os.Stderr, "usage: %s -output file.pprof\n", os.Args[0])
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(*output)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
if err := pprof.StartCPUProfile(f); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
defer pprof.StopCPUProfile()
|
||||||
|
|
||||||
|
// Spin for long enough to collect some samples.
|
||||||
|
start := time.Now()
|
||||||
|
for time.Since(start) < time.Second {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2838,9 +2838,10 @@ func TestTxStmtDeadlock(t *testing.T) {
|
||||||
db := newTestDB(t, "people")
|
db := newTestDB(t, "people")
|
||||||
defer closeDB(t, db)
|
defer closeDB(t, db)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Millisecond)
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
tx, err := db.BeginTx(ctx, nil)
|
tx, err := db.BeginTx(ctx, nil)
|
||||||
|
cancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,16 @@ type Image interface {
|
||||||
Set(x, y int, c color.Color)
|
Set(x, y int, c color.Color)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RGBA64Image extends both the Image and image.RGBA64Image interfaces with a
|
||||||
|
// SetRGBA64 method to change a single pixel. SetRGBA64 is equivalent to
|
||||||
|
// calling Set, but it can avoid allocations from converting concrete color
|
||||||
|
// types to the color.Color interface type.
|
||||||
|
type RGBA64Image interface {
|
||||||
|
image.RGBA64Image
|
||||||
|
Set(x, y int, c color.Color)
|
||||||
|
SetRGBA64(x, y int, c color.RGBA64)
|
||||||
|
}
|
||||||
|
|
||||||
// Quantizer produces a palette for an image.
|
// Quantizer produces a palette for an image.
|
||||||
type Quantizer interface {
|
type Quantizer interface {
|
||||||
// Quantize appends up to cap(p) - len(p) colors to p and returns the
|
// Quantize appends up to cap(p) - len(p) colors to p and returns the
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,17 @@ type Image interface {
|
||||||
At(x, y int) color.Color
|
At(x, y int) color.Color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RGBA64Image is an Image whose pixels can be converted directly to a
|
||||||
|
// color.RGBA64.
|
||||||
|
type RGBA64Image interface {
|
||||||
|
// RGBA64At returns the RGBA64 color of the pixel at (x, y). It is
|
||||||
|
// equivalent to calling At(x, y).RGBA() and converting the resulting
|
||||||
|
// 32-bit return values to a color.RGBA64, but it can avoid allocations
|
||||||
|
// from converting concrete color types to the color.Color interface type.
|
||||||
|
RGBA64At(x, y int) color.RGBA64
|
||||||
|
Image
|
||||||
|
}
|
||||||
|
|
||||||
// PalettedImage is an image whose colors may come from a limited palette.
|
// PalettedImage is an image whose colors may come from a limited palette.
|
||||||
// If m is a PalettedImage and m.ColorModel() returns a color.Palette p,
|
// If m is a PalettedImage and m.ColorModel() returns a color.Palette p,
|
||||||
// then m.At(x, y) should be equivalent to p[m.ColorIndexAt(x, y)]. If m's
|
// then m.At(x, y) should be equivalent to p[m.ColorIndexAt(x, y)]. If m's
|
||||||
|
|
@ -90,6 +101,24 @@ func (p *RGBA) At(x, y int) color.Color {
|
||||||
return p.RGBAAt(x, y)
|
return p.RGBAAt(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *RGBA) RGBA64At(x, y int) color.RGBA64 {
|
||||||
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
|
return color.RGBA64{}
|
||||||
|
}
|
||||||
|
i := p.PixOffset(x, y)
|
||||||
|
s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
|
||||||
|
r := uint16(s[0])
|
||||||
|
g := uint16(s[1])
|
||||||
|
b := uint16(s[2])
|
||||||
|
a := uint16(s[3])
|
||||||
|
return color.RGBA64{
|
||||||
|
(r << 8) | r,
|
||||||
|
(g << 8) | g,
|
||||||
|
(b << 8) | b,
|
||||||
|
(a << 8) | a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *RGBA) RGBAAt(x, y int) color.RGBA {
|
func (p *RGBA) RGBAAt(x, y int) color.RGBA {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return color.RGBA{}
|
return color.RGBA{}
|
||||||
|
|
@ -118,6 +147,18 @@ func (p *RGBA) Set(x, y int, c color.Color) {
|
||||||
s[3] = c1.A
|
s[3] = c1.A
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *RGBA) SetRGBA64(x, y int, c color.RGBA64) {
|
||||||
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i := p.PixOffset(x, y)
|
||||||
|
s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
|
||||||
|
s[0] = uint8(c.R >> 8)
|
||||||
|
s[1] = uint8(c.G >> 8)
|
||||||
|
s[2] = uint8(c.B >> 8)
|
||||||
|
s[3] = uint8(c.A >> 8)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *RGBA) SetRGBA(x, y int, c color.RGBA) {
|
func (p *RGBA) SetRGBA(x, y int, c color.RGBA) {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return
|
return
|
||||||
|
|
@ -311,6 +352,11 @@ func (p *NRGBA) At(x, y int) color.Color {
|
||||||
return p.NRGBAAt(x, y)
|
return p.NRGBAAt(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *NRGBA) RGBA64At(x, y int) color.RGBA64 {
|
||||||
|
r, g, b, a := p.NRGBAAt(x, y).RGBA()
|
||||||
|
return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *NRGBA) NRGBAAt(x, y int) color.NRGBA {
|
func (p *NRGBA) NRGBAAt(x, y int) color.NRGBA {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return color.NRGBA{}
|
return color.NRGBA{}
|
||||||
|
|
@ -339,6 +385,24 @@ func (p *NRGBA) Set(x, y int, c color.Color) {
|
||||||
s[3] = c1.A
|
s[3] = c1.A
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *NRGBA) SetRGBA64(x, y int, c color.RGBA64) {
|
||||||
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r, g, b, a := uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A)
|
||||||
|
if (a != 0) && (a != 0xffff) {
|
||||||
|
r = (r * 0xffff) / a
|
||||||
|
g = (g * 0xffff) / a
|
||||||
|
b = (b * 0xffff) / a
|
||||||
|
}
|
||||||
|
i := p.PixOffset(x, y)
|
||||||
|
s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
|
||||||
|
s[0] = uint8(r >> 8)
|
||||||
|
s[1] = uint8(g >> 8)
|
||||||
|
s[2] = uint8(b >> 8)
|
||||||
|
s[3] = uint8(a >> 8)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *NRGBA) SetNRGBA(x, y int, c color.NRGBA) {
|
func (p *NRGBA) SetNRGBA(x, y int, c color.NRGBA) {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return
|
return
|
||||||
|
|
@ -415,6 +479,11 @@ func (p *NRGBA64) At(x, y int) color.Color {
|
||||||
return p.NRGBA64At(x, y)
|
return p.NRGBA64At(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *NRGBA64) RGBA64At(x, y int) color.RGBA64 {
|
||||||
|
r, g, b, a := p.NRGBA64At(x, y).RGBA()
|
||||||
|
return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *NRGBA64) NRGBA64At(x, y int) color.NRGBA64 {
|
func (p *NRGBA64) NRGBA64At(x, y int) color.NRGBA64 {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return color.NRGBA64{}
|
return color.NRGBA64{}
|
||||||
|
|
@ -452,6 +521,28 @@ func (p *NRGBA64) Set(x, y int, c color.Color) {
|
||||||
s[7] = uint8(c1.A)
|
s[7] = uint8(c1.A)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *NRGBA64) SetRGBA64(x, y int, c color.RGBA64) {
|
||||||
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r, g, b, a := uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A)
|
||||||
|
if (a != 0) && (a != 0xffff) {
|
||||||
|
r = (r * 0xffff) / a
|
||||||
|
g = (g * 0xffff) / a
|
||||||
|
b = (b * 0xffff) / a
|
||||||
|
}
|
||||||
|
i := p.PixOffset(x, y)
|
||||||
|
s := p.Pix[i : i+8 : i+8] // Small cap improves performance, see https://golang.org/issue/27857
|
||||||
|
s[0] = uint8(r >> 8)
|
||||||
|
s[1] = uint8(r)
|
||||||
|
s[2] = uint8(g >> 8)
|
||||||
|
s[3] = uint8(g)
|
||||||
|
s[4] = uint8(b >> 8)
|
||||||
|
s[5] = uint8(b)
|
||||||
|
s[6] = uint8(a >> 8)
|
||||||
|
s[7] = uint8(a)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *NRGBA64) SetNRGBA64(x, y int, c color.NRGBA64) {
|
func (p *NRGBA64) SetNRGBA64(x, y int, c color.NRGBA64) {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return
|
return
|
||||||
|
|
@ -532,6 +623,12 @@ func (p *Alpha) At(x, y int) color.Color {
|
||||||
return p.AlphaAt(x, y)
|
return p.AlphaAt(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Alpha) RGBA64At(x, y int) color.RGBA64 {
|
||||||
|
a := uint16(p.AlphaAt(x, y).A)
|
||||||
|
a |= a << 8
|
||||||
|
return color.RGBA64{a, a, a, a}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Alpha) AlphaAt(x, y int) color.Alpha {
|
func (p *Alpha) AlphaAt(x, y int) color.Alpha {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return color.Alpha{}
|
return color.Alpha{}
|
||||||
|
|
@ -554,6 +651,14 @@ func (p *Alpha) Set(x, y int, c color.Color) {
|
||||||
p.Pix[i] = color.AlphaModel.Convert(c).(color.Alpha).A
|
p.Pix[i] = color.AlphaModel.Convert(c).(color.Alpha).A
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Alpha) SetRGBA64(x, y int, c color.RGBA64) {
|
||||||
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i := p.PixOffset(x, y)
|
||||||
|
p.Pix[i] = uint8(c.A >> 8)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Alpha) SetAlpha(x, y int, c color.Alpha) {
|
func (p *Alpha) SetAlpha(x, y int, c color.Alpha) {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return
|
return
|
||||||
|
|
@ -626,6 +731,11 @@ func (p *Alpha16) At(x, y int) color.Color {
|
||||||
return p.Alpha16At(x, y)
|
return p.Alpha16At(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Alpha16) RGBA64At(x, y int) color.RGBA64 {
|
||||||
|
a := p.Alpha16At(x, y).A
|
||||||
|
return color.RGBA64{a, a, a, a}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Alpha16) Alpha16At(x, y int) color.Alpha16 {
|
func (p *Alpha16) Alpha16At(x, y int) color.Alpha16 {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return color.Alpha16{}
|
return color.Alpha16{}
|
||||||
|
|
@ -650,6 +760,15 @@ func (p *Alpha16) Set(x, y int, c color.Color) {
|
||||||
p.Pix[i+1] = uint8(c1.A)
|
p.Pix[i+1] = uint8(c1.A)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Alpha16) SetRGBA64(x, y int, c color.RGBA64) {
|
||||||
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i := p.PixOffset(x, y)
|
||||||
|
p.Pix[i+0] = uint8(c.A >> 8)
|
||||||
|
p.Pix[i+1] = uint8(c.A)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Alpha16) SetAlpha16(x, y int, c color.Alpha16) {
|
func (p *Alpha16) SetAlpha16(x, y int, c color.Alpha16) {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return
|
return
|
||||||
|
|
@ -723,6 +842,12 @@ func (p *Gray) At(x, y int) color.Color {
|
||||||
return p.GrayAt(x, y)
|
return p.GrayAt(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Gray) RGBA64At(x, y int) color.RGBA64 {
|
||||||
|
gray := uint16(p.GrayAt(x, y).Y)
|
||||||
|
gray |= gray << 8
|
||||||
|
return color.RGBA64{gray, gray, gray, 0xffff}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Gray) GrayAt(x, y int) color.Gray {
|
func (p *Gray) GrayAt(x, y int) color.Gray {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return color.Gray{}
|
return color.Gray{}
|
||||||
|
|
@ -745,6 +870,16 @@ func (p *Gray) Set(x, y int, c color.Color) {
|
||||||
p.Pix[i] = color.GrayModel.Convert(c).(color.Gray).Y
|
p.Pix[i] = color.GrayModel.Convert(c).(color.Gray).Y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Gray) SetRGBA64(x, y int, c color.RGBA64) {
|
||||||
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// This formula is the same as in color.grayModel.
|
||||||
|
gray := (19595*uint32(c.R) + 38470*uint32(c.G) + 7471*uint32(c.B) + 1<<15) >> 24
|
||||||
|
i := p.PixOffset(x, y)
|
||||||
|
p.Pix[i] = uint8(gray)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Gray) SetGray(x, y int, c color.Gray) {
|
func (p *Gray) SetGray(x, y int, c color.Gray) {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return
|
return
|
||||||
|
|
@ -804,6 +939,11 @@ func (p *Gray16) At(x, y int) color.Color {
|
||||||
return p.Gray16At(x, y)
|
return p.Gray16At(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Gray16) RGBA64At(x, y int) color.RGBA64 {
|
||||||
|
gray := p.Gray16At(x, y).Y
|
||||||
|
return color.RGBA64{gray, gray, gray, 0xffff}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Gray16) Gray16At(x, y int) color.Gray16 {
|
func (p *Gray16) Gray16At(x, y int) color.Gray16 {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return color.Gray16{}
|
return color.Gray16{}
|
||||||
|
|
@ -828,6 +968,17 @@ func (p *Gray16) Set(x, y int, c color.Color) {
|
||||||
p.Pix[i+1] = uint8(c1.Y)
|
p.Pix[i+1] = uint8(c1.Y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Gray16) SetRGBA64(x, y int, c color.RGBA64) {
|
||||||
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// This formula is the same as in color.gray16Model.
|
||||||
|
gray := (19595*uint32(c.R) + 38470*uint32(c.G) + 7471*uint32(c.B) + 1<<15) >> 16
|
||||||
|
i := p.PixOffset(x, y)
|
||||||
|
p.Pix[i+0] = uint8(gray >> 8)
|
||||||
|
p.Pix[i+1] = uint8(gray)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Gray16) SetGray16(x, y int, c color.Gray16) {
|
func (p *Gray16) SetGray16(x, y int, c color.Gray16) {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return
|
return
|
||||||
|
|
@ -888,6 +1039,11 @@ func (p *CMYK) At(x, y int) color.Color {
|
||||||
return p.CMYKAt(x, y)
|
return p.CMYKAt(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *CMYK) RGBA64At(x, y int) color.RGBA64 {
|
||||||
|
r, g, b, a := p.CMYKAt(x, y).RGBA()
|
||||||
|
return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *CMYK) CMYKAt(x, y int) color.CMYK {
|
func (p *CMYK) CMYKAt(x, y int) color.CMYK {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return color.CMYK{}
|
return color.CMYK{}
|
||||||
|
|
@ -916,6 +1072,19 @@ func (p *CMYK) Set(x, y int, c color.Color) {
|
||||||
s[3] = c1.K
|
s[3] = c1.K
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *CMYK) SetRGBA64(x, y int, c color.RGBA64) {
|
||||||
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cc, mm, yy, kk := color.RGBToCMYK(uint8(c.R>>8), uint8(c.G>>8), uint8(c.B>>8))
|
||||||
|
i := p.PixOffset(x, y)
|
||||||
|
s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
|
||||||
|
s[0] = cc
|
||||||
|
s[1] = mm
|
||||||
|
s[2] = yy
|
||||||
|
s[3] = kk
|
||||||
|
}
|
||||||
|
|
||||||
func (p *CMYK) SetCMYK(x, y int, c color.CMYK) {
|
func (p *CMYK) SetCMYK(x, y int, c color.CMYK) {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return
|
return
|
||||||
|
|
@ -988,6 +1157,26 @@ func (p *Paletted) At(x, y int) color.Color {
|
||||||
return p.Palette[p.Pix[i]]
|
return p.Palette[p.Pix[i]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Paletted) RGBA64At(x, y int) color.RGBA64 {
|
||||||
|
if len(p.Palette) == 0 {
|
||||||
|
return color.RGBA64{}
|
||||||
|
}
|
||||||
|
c := color.Color(nil)
|
||||||
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
|
c = p.Palette[0]
|
||||||
|
} else {
|
||||||
|
i := p.PixOffset(x, y)
|
||||||
|
c = p.Palette[p.Pix[i]]
|
||||||
|
}
|
||||||
|
r, g, b, a := c.RGBA()
|
||||||
|
return color.RGBA64{
|
||||||
|
uint16(r),
|
||||||
|
uint16(g),
|
||||||
|
uint16(b),
|
||||||
|
uint16(a),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PixOffset returns the index of the first element of Pix that corresponds to
|
// PixOffset returns the index of the first element of Pix that corresponds to
|
||||||
// the pixel at (x, y).
|
// the pixel at (x, y).
|
||||||
func (p *Paletted) PixOffset(x, y int) int {
|
func (p *Paletted) PixOffset(x, y int) int {
|
||||||
|
|
@ -1002,6 +1191,14 @@ func (p *Paletted) Set(x, y int, c color.Color) {
|
||||||
p.Pix[i] = uint8(p.Palette.Index(c))
|
p.Pix[i] = uint8(p.Palette.Index(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Paletted) SetRGBA64(x, y int, c color.RGBA64) {
|
||||||
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i := p.PixOffset(x, y)
|
||||||
|
p.Pix[i] = uint8(p.Palette.Index(c))
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Paletted) ColorIndexAt(x, y int) uint8 {
|
func (p *Paletted) ColorIndexAt(x, y int) uint8 {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return 0
|
return 0
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ package image
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"image/color/palette"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -191,6 +192,80 @@ func Test16BitsPerColorChannel(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRGBA64Image(t *testing.T) {
|
||||||
|
// memset sets every element of s to v.
|
||||||
|
memset := func(s []byte, v byte) {
|
||||||
|
for i := range s {
|
||||||
|
s[i] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r := Rect(0, 0, 3, 2)
|
||||||
|
testCases := []Image{
|
||||||
|
NewAlpha(r),
|
||||||
|
NewAlpha16(r),
|
||||||
|
NewCMYK(r),
|
||||||
|
NewGray(r),
|
||||||
|
NewGray16(r),
|
||||||
|
NewNRGBA(r),
|
||||||
|
NewNRGBA64(r),
|
||||||
|
NewNYCbCrA(r, YCbCrSubsampleRatio444),
|
||||||
|
NewPaletted(r, palette.Plan9),
|
||||||
|
NewRGBA(r),
|
||||||
|
NewRGBA64(r),
|
||||||
|
NewYCbCr(r, YCbCrSubsampleRatio444),
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
switch tc := tc.(type) {
|
||||||
|
// Most of the concrete image types in the testCases implement the
|
||||||
|
// draw.RGBA64Image interface: they have a SetRGBA64 method. We use an
|
||||||
|
// interface literal here, instead of importing "image/draw", to avoid
|
||||||
|
// an import cycle.
|
||||||
|
//
|
||||||
|
// The YCbCr and NYCbCrA types are special-cased. Chroma subsampling
|
||||||
|
// means that setting one pixel can modify neighboring pixels. They
|
||||||
|
// don't have Set or SetRGBA64 methods because that side effect could
|
||||||
|
// be surprising. Here, we just memset the channel buffers instead.
|
||||||
|
case interface {
|
||||||
|
SetRGBA64(x, y int, c color.RGBA64)
|
||||||
|
}:
|
||||||
|
tc.SetRGBA64(1, 1, color.RGBA64{0x7FFF, 0x3FFF, 0x0000, 0x7FFF})
|
||||||
|
|
||||||
|
case *NYCbCrA:
|
||||||
|
memset(tc.YCbCr.Y, 0x77)
|
||||||
|
memset(tc.YCbCr.Cb, 0x88)
|
||||||
|
memset(tc.YCbCr.Cr, 0x99)
|
||||||
|
memset(tc.A, 0xAA)
|
||||||
|
|
||||||
|
case *YCbCr:
|
||||||
|
memset(tc.Y, 0x77)
|
||||||
|
memset(tc.Cb, 0x88)
|
||||||
|
memset(tc.Cr, 0x99)
|
||||||
|
|
||||||
|
default:
|
||||||
|
t.Errorf("could not initialize pixels for %T", tc)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that RGBA64At(x, y) is equivalent to At(x, y).RGBA().
|
||||||
|
rgba64Image, ok := tc.(RGBA64Image)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("%T is not an RGBA64Image", tc)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
got := rgba64Image.RGBA64At(1, 1)
|
||||||
|
wantR, wantG, wantB, wantA := tc.At(1, 1).RGBA()
|
||||||
|
if (uint32(got.R) != wantR) || (uint32(got.G) != wantG) ||
|
||||||
|
(uint32(got.B) != wantB) || (uint32(got.A) != wantA) {
|
||||||
|
t.Errorf("%T:\ngot (0x%04X, 0x%04X, 0x%04X, 0x%04X)\n"+
|
||||||
|
"want (0x%04X, 0x%04X, 0x%04X, 0x%04X)", tc,
|
||||||
|
got.R, got.G, got.B, got.A,
|
||||||
|
wantR, wantG, wantB, wantA)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkAt(b *testing.B) {
|
func BenchmarkAt(b *testing.B) {
|
||||||
for _, tc := range testImages {
|
for _, tc := range testImages {
|
||||||
b.Run(tc.name, func(b *testing.B) {
|
b.Run(tc.name, func(b *testing.B) {
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,11 @@ func (p *YCbCr) At(x, y int) color.Color {
|
||||||
return p.YCbCrAt(x, y)
|
return p.YCbCrAt(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *YCbCr) RGBA64At(x, y int) color.RGBA64 {
|
||||||
|
r, g, b, a := p.YCbCrAt(x, y).RGBA()
|
||||||
|
return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *YCbCr) YCbCrAt(x, y int) color.YCbCr {
|
func (p *YCbCr) YCbCrAt(x, y int) color.YCbCr {
|
||||||
if !(Point{x, y}.In(p.Rect)) {
|
if !(Point{x, y}.In(p.Rect)) {
|
||||||
return color.YCbCr{}
|
return color.YCbCr{}
|
||||||
|
|
@ -210,6 +215,11 @@ func (p *NYCbCrA) At(x, y int) color.Color {
|
||||||
return p.NYCbCrAAt(x, y)
|
return p.NYCbCrAAt(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *NYCbCrA) RGBA64At(x, y int) color.RGBA64 {
|
||||||
|
r, g, b, a := p.NYCbCrAAt(x, y).RGBA()
|
||||||
|
return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *NYCbCrA) NYCbCrAAt(x, y int) color.NYCbCrA {
|
func (p *NYCbCrA) NYCbCrAAt(x, y int) color.NYCbCrA {
|
||||||
if !(Point{X: x, Y: y}.In(p.Rect)) {
|
if !(Point{X: x, Y: y}.In(p.Rect)) {
|
||||||
return color.NYCbCrA{}
|
return color.NYCbCrA{}
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,6 @@
|
||||||
// The default Source is safe for concurrent use by multiple goroutines, but
|
// The default Source is safe for concurrent use by multiple goroutines, but
|
||||||
// Sources created by NewSource are not.
|
// Sources created by NewSource are not.
|
||||||
//
|
//
|
||||||
// Mathematical interval notation such as [0, n) is used throughout the
|
|
||||||
// documentation for this package.
|
|
||||||
//
|
|
||||||
// This package's outputs might be easily predictable regardless of how it's
|
// This package's outputs might be easily predictable regardless of how it's
|
||||||
// seeded. For random numbers suitable for security-sensitive work, see the
|
// seeded. For random numbers suitable for security-sensitive work, see the
|
||||||
// crypto/rand package.
|
// crypto/rand package.
|
||||||
|
|
@ -106,7 +103,7 @@ func (r *Rand) Int() int {
|
||||||
return int(u << 1 >> 1) // clear sign bit if int == int32
|
return int(u << 1 >> 1) // clear sign bit if int == int32
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n).
|
// Int63n returns, as an int64, a non-negative pseudo-random number in the half-open interval [0,n).
|
||||||
// It panics if n <= 0.
|
// It panics if n <= 0.
|
||||||
func (r *Rand) Int63n(n int64) int64 {
|
func (r *Rand) Int63n(n int64) int64 {
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
|
|
@ -123,7 +120,7 @@ func (r *Rand) Int63n(n int64) int64 {
|
||||||
return v % n
|
return v % n
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n).
|
// Int31n returns, as an int32, a non-negative pseudo-random number in the half-open interval [0,n).
|
||||||
// It panics if n <= 0.
|
// It panics if n <= 0.
|
||||||
func (r *Rand) Int31n(n int32) int32 {
|
func (r *Rand) Int31n(n int32) int32 {
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
|
|
@ -140,7 +137,7 @@ func (r *Rand) Int31n(n int32) int32 {
|
||||||
return v % n
|
return v % n
|
||||||
}
|
}
|
||||||
|
|
||||||
// int31n returns, as an int32, a non-negative pseudo-random number in [0,n).
|
// int31n returns, as an int32, a non-negative pseudo-random number in the half-open interval [0,n).
|
||||||
// n must be > 0, but int31n does not check this; the caller must ensure it.
|
// n must be > 0, but int31n does not check this; the caller must ensure it.
|
||||||
// int31n exists because Int31n is inefficient, but Go 1 compatibility
|
// int31n exists because Int31n is inefficient, but Go 1 compatibility
|
||||||
// requires that the stream of values produced by math/rand remain unchanged.
|
// requires that the stream of values produced by math/rand remain unchanged.
|
||||||
|
|
@ -164,7 +161,7 @@ func (r *Rand) int31n(n int32) int32 {
|
||||||
return int32(prod >> 32)
|
return int32(prod >> 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intn returns, as an int, a non-negative pseudo-random number in [0,n).
|
// Intn returns, as an int, a non-negative pseudo-random number in the half-open interval [0,n).
|
||||||
// It panics if n <= 0.
|
// It panics if n <= 0.
|
||||||
func (r *Rand) Intn(n int) int {
|
func (r *Rand) Intn(n int) int {
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
|
|
@ -176,7 +173,7 @@ func (r *Rand) Intn(n int) int {
|
||||||
return int(r.Int63n(int64(n)))
|
return int(r.Int63n(int64(n)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0).
|
// Float64 returns, as a float64, a pseudo-random number in the half-open interval [0.0,1.0).
|
||||||
func (r *Rand) Float64() float64 {
|
func (r *Rand) Float64() float64 {
|
||||||
// A clearer, simpler implementation would be:
|
// A clearer, simpler implementation would be:
|
||||||
// return float64(r.Int63n(1<<53)) / (1<<53)
|
// return float64(r.Int63n(1<<53)) / (1<<53)
|
||||||
|
|
@ -202,7 +199,7 @@ again:
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0).
|
// Float32 returns, as a float32, a pseudo-random number in the half-open interval [0.0,1.0).
|
||||||
func (r *Rand) Float32() float32 {
|
func (r *Rand) Float32() float32 {
|
||||||
// Same rationale as in Float64: we want to preserve the Go 1 value
|
// Same rationale as in Float64: we want to preserve the Go 1 value
|
||||||
// stream except we want to fix it not to return 1.0
|
// stream except we want to fix it not to return 1.0
|
||||||
|
|
@ -215,7 +212,8 @@ again:
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n).
|
// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers
|
||||||
|
// in the half-open interval [0,n).
|
||||||
func (r *Rand) Perm(n int) []int {
|
func (r *Rand) Perm(n int) []int {
|
||||||
m := make([]int, n)
|
m := make([]int, n)
|
||||||
// In the following loop, the iteration when i=0 always swaps m[0] with m[0].
|
// In the following loop, the iteration when i=0 always swaps m[0] with m[0].
|
||||||
|
|
@ -323,31 +321,31 @@ func Int31() int32 { return globalRand.Int31() }
|
||||||
// Int returns a non-negative pseudo-random int from the default Source.
|
// Int returns a non-negative pseudo-random int from the default Source.
|
||||||
func Int() int { return globalRand.Int() }
|
func Int() int { return globalRand.Int() }
|
||||||
|
|
||||||
// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n)
|
// Int63n returns, as an int64, a non-negative pseudo-random number in the half-open interval [0,n)
|
||||||
// from the default Source.
|
// from the default Source.
|
||||||
// It panics if n <= 0.
|
// It panics if n <= 0.
|
||||||
func Int63n(n int64) int64 { return globalRand.Int63n(n) }
|
func Int63n(n int64) int64 { return globalRand.Int63n(n) }
|
||||||
|
|
||||||
// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n)
|
// Int31n returns, as an int32, a non-negative pseudo-random number in the half-open interval [0,n)
|
||||||
// from the default Source.
|
// from the default Source.
|
||||||
// It panics if n <= 0.
|
// It panics if n <= 0.
|
||||||
func Int31n(n int32) int32 { return globalRand.Int31n(n) }
|
func Int31n(n int32) int32 { return globalRand.Int31n(n) }
|
||||||
|
|
||||||
// Intn returns, as an int, a non-negative pseudo-random number in [0,n)
|
// Intn returns, as an int, a non-negative pseudo-random number in the half-open interval [0,n)
|
||||||
// from the default Source.
|
// from the default Source.
|
||||||
// It panics if n <= 0.
|
// It panics if n <= 0.
|
||||||
func Intn(n int) int { return globalRand.Intn(n) }
|
func Intn(n int) int { return globalRand.Intn(n) }
|
||||||
|
|
||||||
// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0)
|
// Float64 returns, as a float64, a pseudo-random number in the half-open interval [0.0,1.0)
|
||||||
// from the default Source.
|
// from the default Source.
|
||||||
func Float64() float64 { return globalRand.Float64() }
|
func Float64() float64 { return globalRand.Float64() }
|
||||||
|
|
||||||
// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0)
|
// Float32 returns, as a float32, a pseudo-random number in the half-open interval [0.0,1.0)
|
||||||
// from the default Source.
|
// from the default Source.
|
||||||
func Float32() float32 { return globalRand.Float32() }
|
func Float32() float32 { return globalRand.Float32() }
|
||||||
|
|
||||||
// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n)
|
// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers
|
||||||
// from the default Source.
|
// in the half-open interval [0,n) from the default Source.
|
||||||
func Perm(n int) []int { return globalRand.Perm(n) }
|
func Perm(n int) []int { return globalRand.Perm(n) }
|
||||||
|
|
||||||
// Shuffle pseudo-randomizes the order of elements using the default Source.
|
// Shuffle pseudo-randomizes the order of elements using the default Source.
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,11 @@ import (
|
||||||
// The current value is set based on flakes observed in the Go builders.
|
// The current value is set based on flakes observed in the Go builders.
|
||||||
var settleTime = 100 * time.Millisecond
|
var settleTime = 100 * time.Millisecond
|
||||||
|
|
||||||
|
// fatalWaitingTime is an absurdly long time to wait for signals to be
|
||||||
|
// delivered but, using it, we (hopefully) eliminate test flakes on the
|
||||||
|
// build servers. See #46736 for discussion.
|
||||||
|
var fatalWaitingTime = 30 * time.Second
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if testenv.Builder() == "solaris-amd64-oraclerel" {
|
if testenv.Builder() == "solaris-amd64-oraclerel" {
|
||||||
// The solaris-amd64-oraclerel builder has been observed to time out in
|
// The solaris-amd64-oraclerel builder has been observed to time out in
|
||||||
|
|
@ -84,7 +89,7 @@ func waitSig1(t *testing.T, c <-chan os.Signal, sig os.Signal, all bool) {
|
||||||
// General user code should filter out all unexpected signals instead of just
|
// General user code should filter out all unexpected signals instead of just
|
||||||
// SIGURG, but since os/signal is tightly coupled to the runtime it seems
|
// SIGURG, but since os/signal is tightly coupled to the runtime it seems
|
||||||
// appropriate to be stricter here.
|
// appropriate to be stricter here.
|
||||||
for time.Since(start) < settleTime {
|
for time.Since(start) < fatalWaitingTime {
|
||||||
select {
|
select {
|
||||||
case s := <-c:
|
case s := <-c:
|
||||||
if s == sig {
|
if s == sig {
|
||||||
|
|
@ -97,7 +102,7 @@ func waitSig1(t *testing.T, c <-chan os.Signal, sig os.Signal, all bool) {
|
||||||
timer.Reset(settleTime / 10)
|
timer.Reset(settleTime / 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Fatalf("timeout after %v waiting for %v", settleTime, sig)
|
t.Fatalf("timeout after %v waiting for %v", fatalWaitingTime, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// quiesce waits until we can be reasonably confident that all pending signals
|
// quiesce waits until we can be reasonably confident that all pending signals
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,15 @@ import (
|
||||||
|
|
||||||
func ExampleFrames() {
|
func ExampleFrames() {
|
||||||
c := func() {
|
c := func() {
|
||||||
// Ask runtime.Callers for up to 10 pcs, including runtime.Callers itself.
|
// Ask runtime.Callers for up to 10 PCs, including runtime.Callers itself.
|
||||||
pc := make([]uintptr, 10)
|
pc := make([]uintptr, 10)
|
||||||
n := runtime.Callers(0, pc)
|
n := runtime.Callers(0, pc)
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
// No pcs available. Stop now.
|
// No PCs available. This can happen if the first argument to
|
||||||
// This can happen if the first argument to runtime.Callers is large.
|
// runtime.Callers is large.
|
||||||
|
//
|
||||||
|
// Return now to avoid processing the zero Frame that would
|
||||||
|
// otherwise be returned by frames.Next below.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,9 +28,12 @@ func ExampleFrames() {
|
||||||
frames := runtime.CallersFrames(pc)
|
frames := runtime.CallersFrames(pc)
|
||||||
|
|
||||||
// Loop to get frames.
|
// Loop to get frames.
|
||||||
// A fixed number of pcs can expand to an indefinite number of Frames.
|
// A fixed number of PCs can expand to an indefinite number of Frames.
|
||||||
for {
|
for {
|
||||||
frame, more := frames.Next()
|
frame, more := frames.Next()
|
||||||
|
|
||||||
|
// Process this frame.
|
||||||
|
//
|
||||||
// To keep this example's output stable
|
// To keep this example's output stable
|
||||||
// even if there are changes in the testing package,
|
// even if there are changes in the testing package,
|
||||||
// stop unwinding when we leave package runtime.
|
// stop unwinding when we leave package runtime.
|
||||||
|
|
@ -35,6 +41,8 @@ func ExampleFrames() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
fmt.Printf("- more:%v | %s\n", more, frame.Function)
|
fmt.Printf("- more:%v | %s\n", more, frame.Function)
|
||||||
|
|
||||||
|
// Check whether there are more frames to process after this one.
|
||||||
if !more {
|
if !more {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,27 @@ func parseProfile(t *testing.T, valBytes []byte, f func(uintptr, []*profile.Loca
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cpuProfilingBroken() bool {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "plan9":
|
||||||
|
// Profiling unimplemented.
|
||||||
|
return true
|
||||||
|
case "aix":
|
||||||
|
// See https://golang.org/issue/45170.
|
||||||
|
return true
|
||||||
|
case "ios", "dragonfly", "netbsd", "illumos", "solaris":
|
||||||
|
// See https://golang.org/issue/13841.
|
||||||
|
return true
|
||||||
|
case "openbsd":
|
||||||
|
if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
|
||||||
|
// See https://golang.org/issue/13841.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// testCPUProfile runs f under the CPU profiler, checking for some conditions specified by need,
|
// testCPUProfile runs f under the CPU profiler, checking for some conditions specified by need,
|
||||||
// as interpreted by matches, and returns the parsed profile.
|
// as interpreted by matches, and returns the parsed profile.
|
||||||
func testCPUProfile(t *testing.T, matches matchFunc, need []string, avoid []string, f func(dur time.Duration)) *profile.Profile {
|
func testCPUProfile(t *testing.T, matches matchFunc, need []string, avoid []string, f func(dur time.Duration)) *profile.Profile {
|
||||||
|
|
@ -276,16 +297,7 @@ func testCPUProfile(t *testing.T, matches matchFunc, need []string, avoid []stri
|
||||||
t.Skip("skipping on plan9")
|
t.Skip("skipping on plan9")
|
||||||
}
|
}
|
||||||
|
|
||||||
broken := false
|
broken := cpuProfilingBroken()
|
||||||
switch runtime.GOOS {
|
|
||||||
// See https://golang.org/issue/45170 for AIX.
|
|
||||||
case "ios", "dragonfly", "netbsd", "illumos", "solaris", "aix":
|
|
||||||
broken = true
|
|
||||||
case "openbsd":
|
|
||||||
if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
|
|
||||||
broken = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
maxDuration := 5 * time.Second
|
maxDuration := 5 * time.Second
|
||||||
if testing.Short() && broken {
|
if testing.Short() && broken {
|
||||||
|
|
@ -612,7 +624,7 @@ func growstack1() {
|
||||||
|
|
||||||
//go:noinline
|
//go:noinline
|
||||||
func growstack(n int) {
|
func growstack(n int) {
|
||||||
var buf [8 << 16]byte
|
var buf [8 << 18]byte
|
||||||
use(buf)
|
use(buf)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
growstack(n - 1)
|
growstack(n - 1)
|
||||||
|
|
@ -620,7 +632,7 @@ func growstack(n int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:noinline
|
//go:noinline
|
||||||
func use(x [8 << 16]byte) {}
|
func use(x [8 << 18]byte) {}
|
||||||
|
|
||||||
func TestBlockProfile(t *testing.T) {
|
func TestBlockProfile(t *testing.T) {
|
||||||
type TestCase struct {
|
type TestCase struct {
|
||||||
|
|
|
||||||
|
|
@ -281,6 +281,8 @@ func setProcessCPUProfiler(hz int32) {
|
||||||
it.it_value = it.it_interval
|
it.it_value = it.it_interval
|
||||||
setitimer(_ITIMER_PROF, &it, nil)
|
setitimer(_ITIMER_PROF, &it, nil)
|
||||||
} else {
|
} else {
|
||||||
|
setitimer(_ITIMER_PROF, &itimerval{}, nil)
|
||||||
|
|
||||||
// If the Go signal handler should be disabled by default,
|
// If the Go signal handler should be disabled by default,
|
||||||
// switch back to the signal handler that was installed
|
// switch back to the signal handler that was installed
|
||||||
// when we enabled profiling. We don't try to handle the case
|
// when we enabled profiling. We don't try to handle the case
|
||||||
|
|
@ -304,8 +306,6 @@ func setProcessCPUProfiler(hz int32) {
|
||||||
setsig(_SIGPROF, h)
|
setsig(_SIGPROF, h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setitimer(_ITIMER_PROF, &itimerval{}, nil)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -383,7 +383,7 @@ func preemptM(mp *m) {
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
func sigFetchG(c *sigctxt) *g {
|
func sigFetchG(c *sigctxt) *g {
|
||||||
switch GOARCH {
|
switch GOARCH {
|
||||||
case "arm", "arm64":
|
case "arm", "arm64", "ppc64", "ppc64le":
|
||||||
if !iscgo && inVDSOPage(c.sigpc()) {
|
if !iscgo && inVDSOPage(c.sigpc()) {
|
||||||
// When using cgo, we save the g on TLS and load it from there
|
// When using cgo, we save the g on TLS and load it from there
|
||||||
// in sigtramp. Just use that.
|
// in sigtramp. Just use that.
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,15 @@ func CallersFrames(callers []uintptr) *Frames {
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next returns frame information for the next caller.
|
// Next returns a Frame representing the next call frame in the slice
|
||||||
// If more is false, there are no more callers (the Frame value is valid).
|
// of PC values. If it has already returned all call frames, Next
|
||||||
|
// returns a zero Frame.
|
||||||
|
//
|
||||||
|
// The more result indicates whether the next call to Next will return
|
||||||
|
// a valid Frame. It does not necessarily indicate whether this call
|
||||||
|
// returned one.
|
||||||
|
//
|
||||||
|
// See the Frames example for idiomatic usage.
|
||||||
func (ci *Frames) Next() (frame Frame, more bool) {
|
func (ci *Frames) Next() (frame Frame, more bool) {
|
||||||
for len(ci.frames) < 2 {
|
for len(ci.frames) < 2 {
|
||||||
// Find the next frame.
|
// Find the next frame.
|
||||||
|
|
|
||||||
|
|
@ -216,15 +216,45 @@ TEXT runtime·walltime(SB),NOSPLIT,$16-12
|
||||||
MOVD (g_sched+gobuf_sp)(R7), R1 // Set SP to g0 stack
|
MOVD (g_sched+gobuf_sp)(R7), R1 // Set SP to g0 stack
|
||||||
|
|
||||||
noswitch:
|
noswitch:
|
||||||
SUB $16, R1 // Space for results
|
SUB $16, R1 // Space for results
|
||||||
RLDICR $0, R1, $59, R1 // Align for C code
|
RLDICR $0, R1, $59, R1 // Align for C code
|
||||||
MOVD R12, CTR
|
MOVD R12, CTR
|
||||||
MOVD R1, R4
|
MOVD R1, R4
|
||||||
BL (CTR) // Call from VDSO
|
|
||||||
MOVD $0, R0 // Restore R0
|
// Store g on gsignal's stack, so if we receive a signal
|
||||||
MOVD 0(R1), R3 // sec
|
// during VDSO code we can find the g.
|
||||||
MOVD 8(R1), R5 // nsec
|
// If we don't have a signal stack, we won't receive signal,
|
||||||
MOVD R15, R1 // Restore SP
|
// so don't bother saving g.
|
||||||
|
// When using cgo, we already saved g on TLS, also don't save
|
||||||
|
// g here.
|
||||||
|
// Also don't save g if we are already on the signal stack.
|
||||||
|
// We won't get a nested signal.
|
||||||
|
MOVBZ runtime·iscgo(SB), R22
|
||||||
|
CMP R22, $0
|
||||||
|
BNE nosaveg
|
||||||
|
MOVD m_gsignal(R21), R22 // g.m.gsignal
|
||||||
|
CMP R22, $0
|
||||||
|
BEQ nosaveg
|
||||||
|
|
||||||
|
CMP g, R22
|
||||||
|
BEQ nosaveg
|
||||||
|
MOVD (g_stack+stack_lo)(R22), R22 // g.m.gsignal.stack.lo
|
||||||
|
MOVD g, (R22)
|
||||||
|
|
||||||
|
BL (CTR) // Call from VDSO
|
||||||
|
|
||||||
|
MOVD $0, (R22) // clear g slot, R22 is unchanged by C code
|
||||||
|
|
||||||
|
JMP finish
|
||||||
|
|
||||||
|
nosaveg:
|
||||||
|
BL (CTR) // Call from VDSO
|
||||||
|
|
||||||
|
finish:
|
||||||
|
MOVD $0, R0 // Restore R0
|
||||||
|
MOVD 0(R1), R3 // sec
|
||||||
|
MOVD 8(R1), R5 // nsec
|
||||||
|
MOVD R15, R1 // Restore SP
|
||||||
|
|
||||||
// Restore vdsoPC, vdsoSP
|
// Restore vdsoPC, vdsoSP
|
||||||
// We don't worry about being signaled between the two stores.
|
// We don't worry about being signaled between the two stores.
|
||||||
|
|
@ -236,7 +266,7 @@ noswitch:
|
||||||
MOVD 32(R1), R6
|
MOVD 32(R1), R6
|
||||||
MOVD R6, m_vdsoPC(R21)
|
MOVD R6, m_vdsoPC(R21)
|
||||||
|
|
||||||
finish:
|
return:
|
||||||
MOVD R3, sec+0(FP)
|
MOVD R3, sec+0(FP)
|
||||||
MOVW R5, nsec+8(FP)
|
MOVW R5, nsec+8(FP)
|
||||||
RET
|
RET
|
||||||
|
|
@ -247,7 +277,7 @@ fallback:
|
||||||
SYSCALL $SYS_clock_gettime
|
SYSCALL $SYS_clock_gettime
|
||||||
MOVD 32(R1), R3
|
MOVD 32(R1), R3
|
||||||
MOVD 40(R1), R5
|
MOVD 40(R1), R5
|
||||||
JMP finish
|
JMP return
|
||||||
|
|
||||||
TEXT runtime·nanotime1(SB),NOSPLIT,$16-8
|
TEXT runtime·nanotime1(SB),NOSPLIT,$16-8
|
||||||
MOVD $1, R3 // CLOCK_MONOTONIC
|
MOVD $1, R3 // CLOCK_MONOTONIC
|
||||||
|
|
@ -283,7 +313,37 @@ noswitch:
|
||||||
RLDICR $0, R1, $59, R1 // Align for C code
|
RLDICR $0, R1, $59, R1 // Align for C code
|
||||||
MOVD R12, CTR
|
MOVD R12, CTR
|
||||||
MOVD R1, R4
|
MOVD R1, R4
|
||||||
BL (CTR) // Call from VDSO
|
|
||||||
|
// Store g on gsignal's stack, so if we receive a signal
|
||||||
|
// during VDSO code we can find the g.
|
||||||
|
// If we don't have a signal stack, we won't receive signal,
|
||||||
|
// so don't bother saving g.
|
||||||
|
// When using cgo, we already saved g on TLS, also don't save
|
||||||
|
// g here.
|
||||||
|
// Also don't save g if we are already on the signal stack.
|
||||||
|
// We won't get a nested signal.
|
||||||
|
MOVBZ runtime·iscgo(SB), R22
|
||||||
|
CMP R22, $0
|
||||||
|
BNE nosaveg
|
||||||
|
MOVD m_gsignal(R21), R22 // g.m.gsignal
|
||||||
|
CMP R22, $0
|
||||||
|
BEQ nosaveg
|
||||||
|
|
||||||
|
CMP g, R22
|
||||||
|
BEQ nosaveg
|
||||||
|
MOVD (g_stack+stack_lo)(R22), R22 // g.m.gsignal.stack.lo
|
||||||
|
MOVD g, (R22)
|
||||||
|
|
||||||
|
BL (CTR) // Call from VDSO
|
||||||
|
|
||||||
|
MOVD $0, (R22) // clear g slot, R22 is unchanged by C code
|
||||||
|
|
||||||
|
JMP finish
|
||||||
|
|
||||||
|
nosaveg:
|
||||||
|
BL (CTR) // Call from VDSO
|
||||||
|
|
||||||
|
finish:
|
||||||
MOVD $0, R0 // Restore R0
|
MOVD $0, R0 // Restore R0
|
||||||
MOVD 0(R1), R3 // sec
|
MOVD 0(R1), R3 // sec
|
||||||
MOVD 8(R1), R5 // nsec
|
MOVD 8(R1), R5 // nsec
|
||||||
|
|
@ -299,7 +359,7 @@ noswitch:
|
||||||
MOVD 32(R1), R6
|
MOVD 32(R1), R6
|
||||||
MOVD R6, m_vdsoPC(R21)
|
MOVD R6, m_vdsoPC(R21)
|
||||||
|
|
||||||
finish:
|
return:
|
||||||
// sec is in R3, nsec in R5
|
// sec is in R3, nsec in R5
|
||||||
// return nsec in R3
|
// return nsec in R3
|
||||||
MOVD $1000000000, R4
|
MOVD $1000000000, R4
|
||||||
|
|
@ -314,7 +374,7 @@ fallback:
|
||||||
SYSCALL $SYS_clock_gettime
|
SYSCALL $SYS_clock_gettime
|
||||||
MOVD 32(R1), R3
|
MOVD 32(R1), R3
|
||||||
MOVD 40(R1), R5
|
MOVD 40(R1), R5
|
||||||
JMP finish
|
JMP return
|
||||||
|
|
||||||
TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28
|
TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28
|
||||||
MOVW how+0(FP), R3
|
MOVW how+0(FP), R3
|
||||||
|
|
@ -469,7 +529,7 @@ TEXT sigtramp<>(SB),NOSPLIT|NOFRAME,$0
|
||||||
// this might be called in external code context,
|
// this might be called in external code context,
|
||||||
// where g is not set.
|
// where g is not set.
|
||||||
MOVBZ runtime·iscgo(SB), R6
|
MOVBZ runtime·iscgo(SB), R6
|
||||||
CMP R6, $0
|
CMP R6, $0
|
||||||
BEQ 2(PC)
|
BEQ 2(PC)
|
||||||
BL runtime·load_g(SB)
|
BL runtime·load_g(SB)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -252,6 +252,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
var initRan bool
|
var initRan bool
|
||||||
|
|
@ -908,11 +910,6 @@ func (c *common) Cleanup(f func()) {
|
||||||
c.cleanups = append(c.cleanups, fn)
|
c.cleanups = append(c.cleanups, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
var tempDirReplacer struct {
|
|
||||||
sync.Once
|
|
||||||
r *strings.Replacer
|
|
||||||
}
|
|
||||||
|
|
||||||
// TempDir returns a temporary directory for the test to use.
|
// TempDir returns a temporary directory for the test to use.
|
||||||
// The directory is automatically removed by Cleanup when the test and
|
// The directory is automatically removed by Cleanup when the test and
|
||||||
// all its subtests complete.
|
// all its subtests complete.
|
||||||
|
|
@ -936,13 +933,26 @@ func (c *common) TempDir() string {
|
||||||
if nonExistent {
|
if nonExistent {
|
||||||
c.Helper()
|
c.Helper()
|
||||||
|
|
||||||
// os.MkdirTemp doesn't like path separators in its pattern,
|
// Drop unusual characters (such as path separators or
|
||||||
// so mangle the name to accommodate subtests.
|
// characters interacting with globs) from the directory name to
|
||||||
tempDirReplacer.Do(func() {
|
// avoid surprising os.MkdirTemp behavior.
|
||||||
tempDirReplacer.r = strings.NewReplacer("/", "_", "\\", "_", ":", "_")
|
mapper := func(r rune) rune {
|
||||||
})
|
if r < utf8.RuneSelf {
|
||||||
pattern := tempDirReplacer.r.Replace(c.Name())
|
const allowed = "!#$%&()+,-.=@^_{}~ "
|
||||||
|
if '0' <= r && r <= '9' ||
|
||||||
|
'a' <= r && r <= 'z' ||
|
||||||
|
'A' <= r && r <= 'Z' {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
if strings.ContainsRune(allowed, r) {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
} else if unicode.IsLetter(r) || unicode.IsNumber(r) {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
pattern := strings.Map(mapper, c.Name())
|
||||||
c.tempDir, c.tempDirErr = os.MkdirTemp("", pattern)
|
c.tempDir, c.tempDirErr = os.MkdirTemp("", pattern)
|
||||||
if c.tempDirErr == nil {
|
if c.tempDirErr == nil {
|
||||||
c.Cleanup(func() {
|
c.Cleanup(func() {
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,9 @@ func TestTempDir(t *testing.T) {
|
||||||
t.Run("test:subtest", testTempDir)
|
t.Run("test:subtest", testTempDir)
|
||||||
t.Run("test/..", testTempDir)
|
t.Run("test/..", testTempDir)
|
||||||
t.Run("../test", testTempDir)
|
t.Run("../test", testTempDir)
|
||||||
|
t.Run("test[]", testTempDir)
|
||||||
|
t.Run("test*", testTempDir)
|
||||||
|
t.Run("äöüéè", testTempDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTempDir(t *testing.T) {
|
func testTempDir(t *testing.T) {
|
||||||
|
|
@ -74,7 +77,7 @@ func testTempDir(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Errorf("directory %q stil exists: %v, isDir=%v", dir, fi, fi.IsDir())
|
t.Errorf("directory %q still exists: %v, isDir=%v", dir, fi, fi.IsDir())
|
||||||
default:
|
default:
|
||||||
if !t.Failed() {
|
if !t.Failed() {
|
||||||
t.Fatal("never received dir channel")
|
t.Fatal("never received dir channel")
|
||||||
|
|
@ -108,6 +111,11 @@ func testTempDir(t *testing.T) {
|
||||||
if len(files) > 0 {
|
if len(files) > 0 {
|
||||||
t.Errorf("unexpected %d files in TempDir: %v", len(files), files)
|
t.Errorf("unexpected %d files in TempDir: %v", len(files), files)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glob := filepath.Join(dir, "*.txt")
|
||||||
|
if _, err := filepath.Glob(glob); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetenv(t *testing.T) {
|
func TestSetenv(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1340,7 +1340,7 @@ func UnixMicro(usec int64) Time {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDST reports whether the time in the configured location is in Daylight Savings Time.
|
// IsDST reports whether the time in the configured location is in Daylight Savings Time.
|
||||||
func (t *Time) IsDST() bool {
|
func (t Time) IsDST() bool {
|
||||||
_, _, _, _, isDST := t.loc.lookup(t.Unix())
|
_, _, _, _, isDST := t.loc.lookup(t.Unix())
|
||||||
return isDST
|
return isDST
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue