aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilippo Valsorda <filippo@golang.org>2020-09-10 18:19:26 +0200
committerFilippo Valsorda <filippo@golang.org>2020-09-10 18:19:26 +0200
commitdbc5602d18397d1841cb7b2e8974d472c15dee83 (patch)
tree99d665cb07f722f28d620bdcb874c8db80b55d53
parenta15df605fc4aa6347494fd55a5c4c6544b0eb981 (diff)
parent9706f510a5e2754595d716bd64be8375997311fb (diff)
downloadgo-dbc5602d18397d1841cb7b2e8974d472c15dee83.tar.gz
go-dbc5602d18397d1841cb7b2e8974d472c15dee83.zip
[dev.boringcrypto.go1.15] all: merge go1.15.2 into dev.boringcrypto.go1.15
Change-Id: Ibf4fe1ff683af56f4c4533cc43b67f73a68311e3
-rw-r--r--doc/go1.14.html6
-rw-r--r--doc/go1.15.html18
-rw-r--r--src/cmd/compile/internal/gc/syntax.go4
-rw-r--r--src/cmd/compile/internal/gc/walk.go7
-rw-r--r--src/cmd/compile/internal/ssa/gen/PPC64Ops.go4
-rw-r--r--src/cmd/compile/internal/ssa/opGen.go2
-rw-r--r--src/cmd/compile/internal/ssa/phiopt.go2
-rw-r--r--src/cmd/compile/internal/ssa/prove.go2
-rw-r--r--src/cmd/go/internal/test/test.go8
-rw-r--r--src/cmd/go/internal/test/testflag.go29
-rw-r--r--src/cmd/go/testdata/script/test_flags.txt35
-rw-r--r--src/cmd/go/testdata/script/test_json_exit.txt102
-rw-r--r--src/cmd/go/testdata/script/test_json_interleaved.txt27
-rw-r--r--src/cmd/go/testdata/script/testing_issue40908.txt21
-rw-r--r--src/cmd/internal/obj/arm/asm5.go3
-rw-r--r--src/cmd/internal/obj/arm/obj5.go46
-rw-r--r--src/cmd/internal/obj/arm64/asm7.go3
-rw-r--r--src/cmd/internal/obj/arm64/obj7.go56
-rw-r--r--src/cmd/internal/obj/mips/asm0.go3
-rw-r--r--src/cmd/internal/obj/mips/obj0.go26
-rw-r--r--src/cmd/internal/obj/ppc64/asm9.go3
-rw-r--r--src/cmd/internal/obj/ppc64/obj9.go11
-rw-r--r--src/cmd/internal/test2json/test2json.go44
-rw-r--r--src/cmd/internal/test2json/testdata/benchshort.json1
-rw-r--r--src/cmd/internal/test2json/testdata/empty.json1
-rw-r--r--src/cmd/test2json/main.go6
-rw-r--r--src/internal/poll/copy_file_range_linux.go10
-rw-r--r--src/net/http/cgi/child.go36
-rw-r--r--src/net/http/cgi/child_test.go69
-rw-r--r--src/net/http/cgi/integration_test.go53
-rw-r--r--src/net/http/fcgi/child.go39
-rw-r--r--src/net/http/fcgi/fcgi_test.go53
-rw-r--r--src/net/mail/message.go13
-rw-r--r--src/net/mail/message_test.go32
-rw-r--r--src/runtime/asm_ppc64x.s41
-rw-r--r--src/runtime/checkptr_test.go1
-rw-r--r--src/runtime/lockrank_off.go10
-rw-r--r--src/runtime/os_windows.go73
-rw-r--r--src/runtime/testdata/testprog/checkptr.go8
-rw-r--r--src/sync/map.go1
-rw-r--r--src/sync/map_test.go24
-rw-r--r--src/testing/testing.go29
-rw-r--r--test/fixedbugs/issue40746.go19
-rw-r--r--test/fixedbugs/issue40917.go23
44 files changed, 775 insertions, 229 deletions
diff --git a/doc/go1.14.html b/doc/go1.14.html
index 35a9f3c2f3..410e0cbf7c 100644
--- a/doc/go1.14.html
+++ b/doc/go1.14.html
@@ -609,6 +609,12 @@ Do not send CLs removing the interior tags from such phrases.
If a program needs to accept invalid numbers like the empty string,
consider wrapping the type with <a href="/pkg/encoding/json/#Unmarshaler"><code>Unmarshaler</code></a>.
</p>
+
+ <p><!-- CL 200237 -->
+ <a href="/pkg/encoding/json/#Unmarshal"><code>Unmarshal</code></a>
+ can now support map keys with string underlying type which implement
+ <a href="/pkg/encoding/#TextUnmarshaler"><code>encoding.TextUnmarshaler</code></a>.
+ </p>
</dd>
</dl><!-- encoding/json -->
diff --git a/doc/go1.15.html b/doc/go1.15.html
index 8872d71138..c691bf3bd5 100644
--- a/doc/go1.15.html
+++ b/doc/go1.15.html
@@ -357,7 +357,10 @@ Do not send CLs removing the interior tags from such phrases.
The linker now defaults to internal linking mode
for <code>-buildmode=pie</code> on
<code>linux/amd64</code> and <code>linux/arm64</code>, so these
- configurations no longer require a C linker.
+ configurations no longer require a C linker. External linking
+ mode (which was the default in Go 1.14 for
+ <code>-buildmode=pie</code>) can still be requested with
+ <code>-ldflags=-linkmode=external</code> flag.
</p>
<h2 id="objdump">Objdump</h2>
@@ -433,6 +436,19 @@ Do not send CLs removing the interior tags from such phrases.
</dd>
</dl><!-- bufio -->
+<dl id="context"><dt><a href="/pkg/context/">context</a></dt>
+ <dd>
+ <p><!-- CL 223777 -->
+ Creating a derived <code>Context</code> using a nil parent is now explicitly
+ disallowed. Any attempt to do so with the
+ <a href="/pkg/context/#WithValue"><code>WithValue</code></a>,
+ <a href="/pkg/context/#WithDeadline"><code>WithDeadline</code></a>, or
+ <a href="/pkg/context/#WithCancel"><code>WithCancel</code></a> functions
+ will cause a panic.
+ </p>
+ </dd>
+</dl><!-- context -->
+
<dl id="crypto"><dt><a href="/pkg/crypto/">crypto</a></dt>
<dd>
<p><!-- CL 231417, CL 225460 -->
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index b658410c53..47e5e59156 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -141,8 +141,8 @@ const (
nodeInitorder, _ // tracks state during init1; two bits
_, _ // second nodeInitorder bit
_, nodeHasBreak
- _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only
- _, nodeImplicit
+ _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only
+ _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND
_, nodeIsDDD // is the argument variadic
_, nodeDiag // already printed error about this
_, nodeColas // OAS resulting from :=
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 1e6d913ae6..b7cf313938 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -968,6 +968,7 @@ opswitch:
case OANDNOT:
n.Left = walkexpr(n.Left, init)
n.Op = OAND
+ n.SetImplicit(true) // for walkCheckPtrArithmetic
n.Right = nod(OBITNOT, n.Right, nil)
n.Right = typecheck(n.Right, ctxExpr)
n.Right = walkexpr(n.Right, init)
@@ -3993,8 +3994,12 @@ func walkCheckPtrArithmetic(n *Node, init *Nodes) *Node {
case OADD:
walk(n.Left)
walk(n.Right)
- case OSUB, OANDNOT:
+ case OSUB:
walk(n.Left)
+ case OAND:
+ if n.Implicit() { // was OANDNOT
+ walk(n.Left)
+ }
case OCONVNOP:
if n.Left.Type.Etype == TUNSAFEPTR {
n.Left = cheapexpr(n.Left, init)
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
index f8bc6cb20b..0261dc283b 100644
--- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
@@ -645,9 +645,9 @@ func init() {
{name: "LoweredAtomicOr8", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true, hasSideEffects: true},
// LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier
- // It preserves R0 through R15, g, and its arguments R20 and R21,
+ // It preserves R0 through R17 (except special registers R1, R2, R11, R12, R13), g, and its arguments R20 and R21,
// but may clobber anything else, including R31 (REGTMP).
- {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R20"), buildReg("R21")}, clobbers: (callerSave &^ buildReg("R0 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R20 R21 g")) | buildReg("R31")}, clobberFlags: true, aux: "Sym", symEffect: "None"},
+ {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R20"), buildReg("R21")}, clobbers: (callerSave &^ buildReg("R0 R3 R4 R5 R6 R7 R8 R9 R10 R14 R15 R16 R17 R20 R21 g")) | buildReg("R31")}, clobberFlags: true, aux: "Sym", symEffect: "None"},
// There are three of these functions so that they can have three different register inputs.
// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index 9efa1bfcc4..8a27c4bb2e 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -26885,7 +26885,7 @@ var opcodeTable = [...]opInfo{
{0, 1048576}, // R20
{1, 2097152}, // R21
},
- clobbers: 576460746931503104, // R16 R17 R18 R19 R22 R23 R24 R25 R26 R27 R28 R29 R31 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+ clobbers: 576460746931312640, // R11 R12 R18 R19 R22 R23 R24 R25 R26 R27 R28 R29 R31 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
},
},
{
diff --git a/src/cmd/compile/internal/ssa/phiopt.go b/src/cmd/compile/internal/ssa/phiopt.go
index 8643fa584c..db7b02275c 100644
--- a/src/cmd/compile/internal/ssa/phiopt.go
+++ b/src/cmd/compile/internal/ssa/phiopt.go
@@ -154,7 +154,7 @@ func phioptint(v *Value, b0 *Block, reverse int) {
}
v.AddArg(a)
- cvt := v.Block.NewValue1(v.Pos, OpCvtBoolToUint8, a.Type, a)
+ cvt := v.Block.NewValue1(v.Pos, OpCvtBoolToUint8, v.Block.Func.Config.Types.UInt8, a)
switch v.Type.Size() {
case 1:
v.reset(OpCopy)
diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go
index 6c6be39d34..ce7d689f93 100644
--- a/src/cmd/compile/internal/ssa/prove.go
+++ b/src/cmd/compile/internal/ssa/prove.go
@@ -1334,7 +1334,7 @@ func removeBranch(b *Block, branch branch) {
// isNonNegative reports whether v is known to be greater or equal to zero.
func isNonNegative(v *Value) bool {
if !v.Type.IsInteger() {
- panic("isNonNegative bad type")
+ v.Fatalf("isNonNegative bad type: %v", v.Type)
}
// TODO: return true if !v.Type.IsSigned()
// SSA isn't type-safe enough to do that now (issue 37753).
diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go
index 873a76aa38..77bfc11fe9 100644
--- a/src/cmd/go/internal/test/test.go
+++ b/src/cmd/go/internal/test/test.go
@@ -1079,9 +1079,13 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
}
var stdout io.Writer = os.Stdout
+ var err error
if testJSON {
json := test2json.NewConverter(lockedStdout{}, a.Package.ImportPath, test2json.Timestamp)
- defer json.Close()
+ defer func() {
+ json.Exited(err)
+ json.Close()
+ }()
stdout = json
}
@@ -1185,7 +1189,7 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
}
t0 := time.Now()
- err := cmd.Start()
+ err = cmd.Start()
// This is a last-ditch deadline to detect and
// stop wedged test binaries, to keep the builders
diff --git a/src/cmd/go/internal/test/testflag.go b/src/cmd/go/internal/test/testflag.go
index 1ff34f7445..4f0a8924f1 100644
--- a/src/cmd/go/internal/test/testflag.go
+++ b/src/cmd/go/internal/test/testflag.go
@@ -214,9 +214,13 @@ func testFlags(args []string) (packageNames, passToTest []string) {
explicitArgs := make([]string, 0, len(args))
inPkgList := false
+ afterFlagWithoutValue := false
for len(args) > 0 {
f, remainingArgs, err := cmdflag.ParseOne(&CmdTest.Flag, args)
+ wasAfterFlagWithoutValue := afterFlagWithoutValue
+ afterFlagWithoutValue = false // provisionally
+
if errors.Is(err, flag.ErrHelp) {
exitWithUsage()
}
@@ -233,10 +237,24 @@ func testFlags(args []string) (packageNames, passToTest []string) {
if nf := (cmdflag.NonFlagError{}); errors.As(err, &nf) {
if !inPkgList && packageNames != nil {
// We already saw the package list previously, and this argument is not
- // a flag, so it — and everything after it — must be a literal argument
- // to the test binary.
- explicitArgs = append(explicitArgs, args...)
- break
+ // a flag, so it — and everything after it — must be either a value for
+ // a preceding flag or a literal argument to the test binary.
+ if wasAfterFlagWithoutValue {
+ // This argument could syntactically be a flag value, so
+ // optimistically assume that it is and keep looking for go command
+ // flags after it.
+ //
+ // (If we're wrong, we'll at least be consistent with historical
+ // behavior; see https://golang.org/issue/40763.)
+ explicitArgs = append(explicitArgs, nf.RawArg)
+ args = remainingArgs
+ continue
+ } else {
+ // This argument syntactically cannot be a flag value, so it must be a
+ // positional argument, and so must everything after it.
+ explicitArgs = append(explicitArgs, args...)
+ break
+ }
}
inPkgList = true
@@ -272,6 +290,9 @@ func testFlags(args []string) (packageNames, passToTest []string) {
explicitArgs = append(explicitArgs, nd.RawArg)
args = remainingArgs
+ if !nd.HasValue {
+ afterFlagWithoutValue = true
+ }
continue
}
diff --git a/src/cmd/go/testdata/script/test_flags.txt b/src/cmd/go/testdata/script/test_flags.txt
index d38e37f238..63385e6997 100644
--- a/src/cmd/go/testdata/script/test_flags.txt
+++ b/src/cmd/go/testdata/script/test_flags.txt
@@ -10,7 +10,7 @@ stdout '\Aok\s+example.com/x\s+[0-9.s]+\n\z'
! stderr .
# For backward-compatibility with previous releases of the 'go' command,
-# arguments that appear after unrecognized flags should not be treated
+# arguments that appear after unrecognized flags should not be treated
# as packages, even if they are unambiguously not arguments to flags.
# Even though ./x looks like a package path, the real package should be
# the implicit '.'.
@@ -18,6 +18,22 @@ stdout '\Aok\s+example.com/x\s+[0-9.s]+\n\z'
stderr '^no Go files in .+$'
! stderr '/x'
+# However, *flags* that appear after unrecognized flags should still be
+# interpreted as flags, under the (possibly-erroneous) assumption that
+# unrecognized flags are non-boolean.
+
+go test -v -x ./x -timeout 24h -boolflag=true foo -timeout 25h
+stdout 'args: foo -timeout 25h'
+stdout 'timeout: 24h0m0s$' # -timeout is unambiguously not a flag, so the real flag wins.
+
+go test -v -x ./x -timeout 24h -boolflag foo -timeout 25h
+stdout 'args: foo -test\.timeout=25h0m0s' # For legacy reasons, '-timeout ' is erroneously rewritten to -test.timeout; see https://golang.org/issue/40763.
+stdout 'timeout: 24h0m0s$' # Actual flag wins.
+
+go test -v -x ./x -timeout 24h -stringflag foo -timeout 25h
+stdout 'args: $'
+stdout 'timeout: 25h0m0s$' # Later flag wins.
+
# An explicit '-outputdir=' argument should set test.outputdir
# to the 'go' command's working directory, not zero it out
# for the test binary.
@@ -30,23 +46,23 @@ exists ./cover.out
# with the 'test.' prefix in the GOFLAGS entry...
env GOFLAGS='-test.timeout=24h0m0s -count=1'
go test -v -x ./x
-stdout '.*: 24h0m0s$'
+stdout 'timeout: 24h0m0s$'
stderr '-test.count=1'
# ...or without.
env GOFLAGS='-timeout=24h0m0s -count=1'
go test -v -x ./x
-stdout '.*: 24h0m0s$'
+stdout 'timeout: 24h0m0s$'
stderr '-test.count=1'
# Arguments from the command line should override GOFLAGS...
go test -v -x -timeout=25h0m0s ./x
-stdout '.*: 25h0m0s$'
+stdout 'timeout: 25h0m0s$'
stderr '-test.count=1'
# ...even if they use a different flag name.
go test -v -x -test.timeout=26h0m0s ./x
-stdout '.*: 26h0m0s$'
+stdout 'timeout: 26h0m0s$'
stderr '-test\.timeout=26h0m0s'
! stderr 'timeout=24h0m0s'
stderr '-test.count=1'
@@ -99,11 +115,18 @@ package x
import (
"flag"
+ "strings"
"testing"
)
var _ = flag.String("usage_message", "", "dummy flag to check usage message")
+var boolflag = flag.Bool("boolflag", false, "ignored boolean flag")
+var stringflag = flag.String("stringflag", "", "ignored string flag")
func TestLogTimeout(t *testing.T) {
- t.Log(flag.Lookup("test.timeout").Value)
+ t.Logf("timeout: %v", flag.Lookup("test.timeout").Value)
+}
+
+func TestLogArgs(t *testing.T) {
+ t.Logf("args: %s", strings.Join(flag.Args(), " "))
}
diff --git a/src/cmd/go/testdata/script/test_json_exit.txt b/src/cmd/go/testdata/script/test_json_exit.txt
new file mode 100644
index 0000000000..dc7ffb06cf
--- /dev/null
+++ b/src/cmd/go/testdata/script/test_json_exit.txt
@@ -0,0 +1,102 @@
+[short] skip
+
+go test -c -o mainpanic.exe ./mainpanic &
+go test -c -o mainexit0.exe ./mainexit0 &
+go test -c -o testpanic.exe ./testpanic &
+go test -c -o testbgpanic.exe ./testbgpanic &
+wait
+
+# Test binaries that panic in TestMain should be marked as failing.
+
+! go test -json ./mainpanic
+stdout '"Action":"fail"'
+! stdout '"Action":"pass"'
+
+! go tool test2json ./mainpanic.exe
+stdout '"Action":"fail"'
+! stdout '"Action":"pass"'
+
+# Test binaries that exit with status 0 should be marked as passing.
+
+go test -json ./mainexit0
+stdout '"Action":"pass"'
+! stdout '"Action":"fail"'
+
+go tool test2json ./mainexit0.exe
+stdout '"Action":"pass"'
+! stdout '"Action":"fail"'
+
+# Test functions that panic should never be marked as passing
+# (https://golang.org/issue/40132).
+
+! go test -json ./testpanic
+stdout '"Action":"fail"'
+! stdout '"Action":"pass"'
+
+! go tool test2json ./testpanic.exe -test.v
+stdout '"Action":"fail"'
+! stdout '"Action":"pass"'
+
+! go tool test2json ./testpanic.exe
+stdout '"Action":"fail"'
+! stdout '"Action":"pass"'
+
+# Tests that panic in a background goroutine should be marked as failing.
+
+! go test -json ./testbgpanic
+stdout '"Action":"fail"'
+! stdout '"Action":"pass"'
+
+! go tool test2json ./testbgpanic.exe -test.v
+stdout '"Action":"fail"'
+! stdout '"Action":"pass"'
+
+! go tool test2json ./testbgpanic.exe
+stdout '"Action":"fail"'
+! stdout '"Action":"pass"'
+
+-- go.mod --
+module m
+go 1.14
+-- mainpanic/mainpanic_test.go --
+package mainpanic_test
+
+import "testing"
+
+func TestMain(m *testing.M) {
+ panic("haha no")
+}
+-- mainexit0/mainexit0_test.go --
+package mainexit0_test
+
+import (
+ "fmt"
+ "os"
+ "testing"
+)
+
+func TestMain(m *testing.M) {
+ fmt.Println("nothing to do")
+ os.Exit(0)
+}
+-- testpanic/testpanic_test.go --
+package testpanic_test
+
+import "testing"
+
+func TestPanic(*testing.T) {
+ panic("haha no")
+}
+-- testbgpanic/testbgpanic_test.go --
+package testbgpanic_test
+
+import "testing"
+
+func TestPanicInBackground(*testing.T) {
+ c := make(chan struct{})
+ go func() {
+ panic("haha no")
+ close(c)
+ }()
+ <-c
+}
diff --git a/src/cmd/go/testdata/script/test_json_interleaved.txt b/src/cmd/go/testdata/script/test_json_interleaved.txt
new file mode 100644
index 0000000000..e2d349e3fb
--- /dev/null
+++ b/src/cmd/go/testdata/script/test_json_interleaved.txt
@@ -0,0 +1,27 @@
+# Regression test for https://golang.org/issue/40657: output from the main test
+# function should be attributed correctly even if interleaved with the PAUSE
+# line for a new parallel subtest.
+
+[short] skip
+
+go test -json
+stdout '"Test":"TestWeirdTiming","Output":"[^"]* logging to outer again\\n"'
+
+-- go.mod --
+module example.com
+go 1.15
+-- main_test.go --
+package main
+
+import (
+ "testing"
+)
+
+func TestWeirdTiming(outer *testing.T) {
+ outer.Run("pauser", func(pauser *testing.T) {
+ outer.Logf("logging to outer")
+ pauser.Parallel()
+ })
+
+ outer.Logf("logging to outer again")
+}
diff --git a/src/cmd/go/testdata/script/testing_issue40908.txt b/src/cmd/go/testdata/script/testing_issue40908.txt
new file mode 100644
index 0000000000..4939de080c
--- /dev/null
+++ b/src/cmd/go/testdata/script/testing_issue40908.txt
@@ -0,0 +1,21 @@
+[short] skip
+[!race] skip
+
+go test -race testrace
+
+-- testrace/race_test.go --
+package testrace
+
+import "testing"
+
+func TestRace(t *testing.T) {
+ helperDone := make(chan struct{})
+ go func() {
+ t.Logf("Something happened before cleanup.")
+ close(helperDone)
+ }()
+
+ t.Cleanup(func() {
+ <-helperDone
+ })
+}
diff --git a/src/cmd/internal/obj/arm/asm5.go b/src/cmd/internal/obj/arm/asm5.go
index f66f8aaf84..7b7e42ee2e 100644
--- a/src/cmd/internal/obj/arm/asm5.go
+++ b/src/cmd/internal/obj/arm/asm5.go
@@ -327,6 +327,9 @@ var optab = []Optab{
{obj.APCDATA, C_LCON, C_NONE, C_LCON, 0, 0, 0, 0, 0, 0},
{obj.AFUNCDATA, C_LCON, C_NONE, C_ADDR, 0, 0, 0, 0, 0, 0},
{obj.ANOP, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0, 0},
+ {obj.ANOP, C_LCON, C_NONE, C_NONE, 0, 0, 0, 0, 0, 0}, // nop variants, see #40689
+ {obj.ANOP, C_REG, C_NONE, C_NONE, 0, 0, 0, 0, 0, 0},
+ {obj.ANOP, C_FREG, C_NONE, C_NONE, 0, 0, 0, 0, 0, 0},
{obj.ADUFFZERO, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0, 0}, // same as ABL
{obj.ADUFFCOPY, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0, 0}, // same as ABL
{obj.AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0, 0, 0, 0},
diff --git a/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go
index 008118c47b..86831f2b44 100644
--- a/src/cmd/internal/obj/arm/obj5.go
+++ b/src/cmd/internal/obj/arm/obj5.go
@@ -276,67 +276,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
/*
* find leaf subroutines
- * strip NOPs
- * expand RET
- * expand BECOME pseudo
*/
- var q1 *obj.Prog
- var q *obj.Prog
for p := cursym.Func.Text; p != nil; p = p.Link {
switch p.As {
case obj.ATEXT:
p.Mark |= LEAF
- case obj.ARET:
- break
-
case ADIV, ADIVU, AMOD, AMODU:
- q = p
cursym.Func.Text.Mark &^= LEAF
- continue
-
- case obj.ANOP:
- q1 = p.Link
- q.Link = q1 /* q is non-nop */
- if q1 != nil {
- q1.Mark |= p.Mark
- }
- continue
case ABL,
ABX,
obj.ADUFFZERO,
obj.ADUFFCOPY:
cursym.Func.Text.Mark &^= LEAF
- fallthrough
-
- case AB,
- ABEQ,
- ABNE,
- ABCS,
- ABHS,
- ABCC,
- ABLO,
- ABMI,
- ABPL,
- ABVS,
- ABVC,
- ABHI,
- ABLS,
- ABGE,
- ABLT,
- ABGT,
- ABLE:
- q1 = p.Pcond
- if q1 != nil {
- for q1.As == obj.ANOP {
- q1 = q1.Link
- p.Pcond = q1
- }
- }
}
-
- q = p
}
var q2 *obj.Prog
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index df17729a76..f18799b60a 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -837,6 +837,9 @@ var optab = []Optab{
{obj.APCDATA, C_VCON, C_NONE, C_NONE, C_VCON, 0, 0, 0, 0, 0},
{obj.AFUNCDATA, C_VCON, C_NONE, C_NONE, C_ADDR, 0, 0, 0, 0, 0},
{obj.ANOP, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0},
+ {obj.ANOP, C_LCON, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, // nop variants, see #40689
+ {obj.ANOP, C_REG, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0},
+ {obj.ANOP, C_VREG, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0},
{obj.ADUFFZERO, C_NONE, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0}, // same as AB/ABL
{obj.ADUFFCOPY, C_NONE, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0}, // same as AB/ABL
{obj.APCALIGN, C_LCON, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, // align code
diff --git a/src/cmd/internal/obj/arm64/obj7.go b/src/cmd/internal/obj/arm64/obj7.go
index b046685ada..0d74430053 100644
--- a/src/cmd/internal/obj/arm64/obj7.go
+++ b/src/cmd/internal/obj/arm64/obj7.go
@@ -468,73 +468,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
/*
* find leaf subroutines
- * strip NOPs
- * expand RET
*/
- q := (*obj.Prog)(nil)
- var q1 *obj.Prog
for p := c.cursym.Func.Text; p != nil; p = p.Link {
switch p.As {
case obj.ATEXT:
p.Mark |= LEAF
- case obj.ARET:
- break
-
- case obj.ANOP:
- if p.Link != nil {
- q1 = p.Link
- q.Link = q1 /* q is non-nop */
- q1.Mark |= p.Mark
- }
- continue
-
case ABL,
obj.ADUFFZERO,
obj.ADUFFCOPY:
c.cursym.Func.Text.Mark &^= LEAF
- fallthrough
-
- case ACBNZ,
- ACBZ,
- ACBNZW,
- ACBZW,
- ATBZ,
- ATBNZ,
- AB,
- ABEQ,
- ABNE,
- ABCS,
- ABHS,
- ABCC,
- ABLO,
- ABMI,
- ABPL,
- ABVS,
- ABVC,
- ABHI,
- ABLS,
- ABGE,
- ABLT,
- ABGT,
- ABLE,
- AADR, /* strange */
- AADRP:
- q1 = p.Pcond
-
- if q1 != nil {
- for q1.As == obj.ANOP {
- q1 = q1.Link
- p.Pcond = q1
- }
- }
-
- break
}
-
- q = p
}
+ var q *obj.Prog
+ var q1 *obj.Prog
var retjmp *obj.LSym
for p := c.cursym.Func.Text; p != nil; p = p.Link {
o := p.As
diff --git a/src/cmd/internal/obj/mips/asm0.go b/src/cmd/internal/obj/mips/asm0.go
index faa12bf133..faa827da9f 100644
--- a/src/cmd/internal/obj/mips/asm0.go
+++ b/src/cmd/internal/obj/mips/asm0.go
@@ -391,6 +391,9 @@ var optab = []Optab{
{obj.APCDATA, C_LCON, C_NONE, C_LCON, 0, 0, 0, 0, 0},
{obj.AFUNCDATA, C_SCON, C_NONE, C_ADDR, 0, 0, 0, 0, 0},
{obj.ANOP, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0},
+ {obj.ANOP, C_LCON, C_NONE, C_NONE, 0, 0, 0, 0, 0}, // nop variants, see #40689
+ {obj.ANOP, C_REG, C_NONE, C_NONE, 0, 0, 0, 0, 0},
+ {obj.ANOP, C_FREG, C_NONE, C_NONE, 0, 0, 0, 0, 0},
{obj.ADUFFZERO, C_NONE, C_NONE, C_LBRA, 11, 4, 0, 0, 0}, // same as AJMP
{obj.ADUFFCOPY, C_NONE, C_NONE, C_LBRA, 11, 4, 0, 0, 0}, // same as AJMP
diff --git a/src/cmd/internal/obj/mips/obj0.go b/src/cmd/internal/obj/mips/obj0.go
index 3106143844..77cad979a6 100644
--- a/src/cmd/internal/obj/mips/obj0.go
+++ b/src/cmd/internal/obj/mips/obj0.go
@@ -158,19 +158,14 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
/*
* find leaf subroutines
- * strip NOPs
* expand RET
* expand BECOME pseudo
*/
- var q *obj.Prog
- var q1 *obj.Prog
for p := c.cursym.Func.Text; p != nil; p = p.Link {
switch p.As {
/* too hard, just leave alone */
case obj.ATEXT:
- q = p
-
p.Mark |= LABEL | LEAF | SYNC
if p.Link != nil {
p.Link.Mark |= LABEL
@@ -179,7 +174,6 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
/* too hard, just leave alone */
case AMOVW,
AMOVV:
- q = p
if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL {
p.Mark |= LABEL | SYNC
break
@@ -195,11 +189,9 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
ATLBWI,
ATLBP,
ATLBR:
- q = p
p.Mark |= LABEL | SYNC
case ANOR:
- q = p
if p.To.Type == obj.TYPE_REG {
if p.To.Reg == REGZERO {
p.Mark |= LABEL | SYNC
@@ -235,8 +227,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
} else {
p.Mark |= BRANCH
}
- q = p
- q1 = p.Pcond
+ q1 := p.Pcond
if q1 != nil {
for q1.As == obj.ANOP {
q1 = q1.Link
@@ -254,24 +245,11 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
if q1 != nil {
q1.Mark |= LABEL
}
- continue
case ARET:
- q = p
if p.Link != nil {
p.Link.Mark |= LABEL
}
- continue
-
- case obj.ANOP:
- q1 = p.Link
- q.Link = q1 /* q is non-nop */
- q1.Mark |= p.Mark
- continue
-
- default:
- q = p
- continue
}
}
@@ -284,6 +262,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
mov = AMOVW
}
+ var q *obj.Prog
+ var q1 *obj.Prog
autosize := int32(0)
var p1 *obj.Prog
var p2 *obj.Prog
diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go
index 0fd0744a42..238ca8f0b7 100644
--- a/src/cmd/internal/obj/ppc64/asm9.go
+++ b/src/cmd/internal/obj/ppc64/asm9.go
@@ -613,6 +613,9 @@ var optab = []Optab{
{obj.APCDATA, C_LCON, C_NONE, C_NONE, C_LCON, 0, 0, 0},
{obj.AFUNCDATA, C_SCON, C_NONE, C_NONE, C_ADDR, 0, 0, 0},
{obj.ANOP, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0},
+ {obj.ANOP, C_LCON, C_NONE, C_NONE, C_NONE, 0, 0, 0}, // NOP operand variations added for #40689
+ {obj.ANOP, C_REG, C_NONE, C_NONE, C_NONE, 0, 0, 0}, // to preserve previous behavior
+ {obj.ANOP, C_FREG, C_NONE, C_NONE, C_NONE, 0, 0, 0},
{obj.ADUFFZERO, C_NONE, C_NONE, C_NONE, C_LBRA, 11, 4, 0}, // same as ABR/ABL
{obj.ADUFFCOPY, C_NONE, C_NONE, C_NONE, C_LBRA, 11, 4, 0}, // same as ABR/ABL
{obj.APCALIGN, C_LCON, C_NONE, C_NONE, C_NONE, 0, 0, 0}, // align code
diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go
index 7135488f9d..a2f16f2e2a 100644
--- a/src/cmd/internal/obj/ppc64/obj9.go
+++ b/src/cmd/internal/obj/ppc64/obj9.go
@@ -427,7 +427,6 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
/*
* find leaf subroutines
- * strip NOPs
* expand RET
* expand BECOME pseudo
*/
@@ -557,10 +556,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
q = p
q1 = p.Pcond
if q1 != nil {
- for q1.As == obj.ANOP {
- q1 = q1.Link
- p.Pcond = q1
- }
+ // NOPs are not removed due to #40689.
if q1.Mark&LEAF == 0 {
q1.Mark |= LABEL
@@ -587,9 +583,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
continue
case obj.ANOP:
- q1 = p.Link
- q.Link = q1 /* q is non-nop */
- q1.Mark |= p.Mark
+ // NOPs are not removed due to
+ // #40689
continue
default:
diff --git a/src/cmd/internal/test2json/test2json.go b/src/cmd/internal/test2json/test2json.go
index a01a8900e8..4eb6dd4838 100644
--- a/src/cmd/internal/test2json/test2json.go
+++ b/src/cmd/internal/test2json/test2json.go
@@ -45,10 +45,10 @@ type textBytes []byte
func (b textBytes) MarshalText() ([]byte, error) { return b, nil }
-// A converter holds the state of a test-to-JSON conversion.
+// A Converter holds the state of a test-to-JSON conversion.
// It implements io.WriteCloser; the caller writes test output in,
// and the converter writes JSON output to w.
-type converter struct {
+type Converter struct {
w io.Writer // JSON output stream
pkg string // package to name in events
mode Mode // mode bits
@@ -100,9 +100,9 @@ var (
//
// The pkg string, if present, specifies the import path to
// report in the JSON stream.
-func NewConverter(w io.Writer, pkg string, mode Mode) io.WriteCloser {
- c := new(converter)
- *c = converter{
+func NewConverter(w io.Writer, pkg string, mode Mode) *Converter {
+ c := new(Converter)
+ *c = Converter{
w: w,
pkg: pkg,
mode: mode,
@@ -122,11 +122,20 @@ func NewConverter(w io.Writer, pkg string, mode Mode) io.WriteCloser {
}
// Write writes the test input to the converter.
-func (c *converter) Write(b []byte) (int, error) {
+func (c *Converter) Write(b []byte) (int, error) {
c.input.write(b)
return len(b), nil
}
+// Exited marks the test process as having exited with the given error.
+func (c *Converter) Exited(err error) {
+ if err == nil {
+ c.result = "pass"
+ } else {
+ c.result = "fail"
+ }
+}
+
var (
// printed by test on successful run.
bigPass = []byte("PASS\n")
@@ -160,7 +169,7 @@ var (
// handleInputLine handles a single whole test output line.
// It must write the line to c.output but may choose to do so
// before or after emitting other events.
-func (c *converter) handleInputLine(line []byte) {
+func (c *Converter) handleInputLine(line []byte) {
// Final PASS or FAIL.
if bytes.Equal(line, bigPass) || bytes.Equal(line, bigFail) || bytes.HasPrefix(line, bigFailErrorPrefix) {
c.flushReport(0)
@@ -286,7 +295,7 @@ func (c *converter) handleInputLine(line []byte) {
}
// flushReport flushes all pending PASS/FAIL reports at levels >= depth.
-func (c *converter) flushReport(depth int) {
+func (c *Converter) flushReport(depth int) {
c.testName = ""
for len(c.report) > depth {
e := c.report[len(c.report)-1]
@@ -298,23 +307,22 @@ func (c *converter) flushReport(depth int) {
// Close marks the end of the go test output.
// It flushes any pending input and then output (only partial lines at this point)
// and then emits the final overall package-level pass/fail event.
-func (c *converter) Close() error {
+func (c *Converter) Close() error {
c.input.flush()
c.output.flush()
- e := &event{Action: "pass"}
if c.result != "" {
- e.Action = c.result
- }
- if c.mode&Timestamp != 0 {
- dt := time.Since(c.start).Round(1 * time.Millisecond).Seconds()
- e.Elapsed = &dt
+ e := &event{Action: c.result}
+ if c.mode&Timestamp != 0 {
+ dt := time.Since(c.start).Round(1 * time.Millisecond).Seconds()
+ e.Elapsed = &dt
+ }
+ c.writeEvent(e)
}
- c.writeEvent(e)
return nil
}
// writeOutputEvent writes a single output event with the given bytes.
-func (c *converter) writeOutputEvent(out []byte) {
+func (c *Converter) writeOutputEvent(out []byte) {
c.writeEvent(&event{
Action: "output",
Output: (*textBytes)(&out),
@@ -323,7 +331,7 @@ func (c *converter) writeOutputEvent(out []byte) {
// writeEvent writes a single event.
// It adds the package, time (if requested), and test name (if needed).
-func (c *converter) writeEvent(e *event) {
+func (c *Converter) writeEvent(e *event) {
e.Package = c.pkg
if c.mode&Timestamp != 0 {
t := time.Now()
diff --git a/src/cmd/internal/test2json/testdata/benchshort.json b/src/cmd/internal/test2json/testdata/benchshort.json
index 28e287c848..34b03b9362 100644
--- a/src/cmd/internal/test2json/testdata/benchshort.json
+++ b/src/cmd/internal/test2json/testdata/benchshort.json
@@ -4,4 +4,3 @@
{"Action":"output","Output":"# but to avoid questions of timing, we just use a file with no \\n at all.\n"}
{"Action":"output","Output":"BenchmarkFoo \t"}
{"Action":"output","Output":"10000 early EOF"}
-{"Action":"pass"}
diff --git a/src/cmd/internal/test2json/testdata/empty.json b/src/cmd/internal/test2json/testdata/empty.json
index 80b5217501..e69de29bb2 100644
--- a/src/cmd/internal/test2json/testdata/empty.json
+++ b/src/cmd/internal/test2json/testdata/empty.json
@@ -1 +0,0 @@
-{"Action":"pass"}
diff --git a/src/cmd/test2json/main.go b/src/cmd/test2json/main.go
index 0385d8f246..57a874193e 100644
--- a/src/cmd/test2json/main.go
+++ b/src/cmd/test2json/main.go
@@ -118,12 +118,16 @@ func main() {
w := &countWriter{0, c}
cmd.Stdout = w
cmd.Stderr = w
- if err := cmd.Run(); err != nil {
+ err := cmd.Run()
+ if err != nil {
if w.n > 0 {
// Assume command printed why it failed.
} else {
fmt.Fprintf(c, "test2json: %v\n", err)
}
+ }
+ c.Exited(err)
+ if err != nil {
c.Close()
os.Exit(1)
}
diff --git a/src/internal/poll/copy_file_range_linux.go b/src/internal/poll/copy_file_range_linux.go
index 604607f774..09de299ff7 100644
--- a/src/internal/poll/copy_file_range_linux.go
+++ b/src/internal/poll/copy_file_range_linux.go
@@ -41,7 +41,7 @@ func CopyFileRange(dst, src *FD, remain int64) (written int64, handled bool, err
// use copy_file_range(2) again.
atomic.StoreInt32(&copyFileRangeSupported, 0)
return 0, false, nil
- case syscall.EXDEV, syscall.EINVAL:
+ case syscall.EXDEV, syscall.EINVAL, syscall.EOPNOTSUPP, syscall.EPERM:
// Prior to Linux 5.3, it was not possible to
// copy_file_range across file systems. Similarly to
// the ENOSYS case above, if we see EXDEV, we have
@@ -52,6 +52,14 @@ func CopyFileRange(dst, src *FD, remain int64) (written int64, handled bool, err
// dst or src refer to a pipe rather than a regular
// file. This is another case where no data has been
// transfered, so we consider it unhandled.
+ //
+ // If the file is on NFS, we can see EOPNOTSUPP.
+ // See issue #40731.
+ //
+ // If the process is running inside a Docker container,
+ // we might see EPERM instead of ENOSYS. See issue
+ // #40893. Since EPERM might also be a legitimate error,
+ // don't mark copy_file_range(2) as unsupported.
return 0, false, nil
case nil:
if n == 0 {
diff --git a/src/net/http/cgi/child.go b/src/net/http/cgi/child.go
index 9474175f17..61de6165f6 100644
--- a/src/net/http/cgi/child.go
+++ b/src/net/http/cgi/child.go
@@ -163,10 +163,12 @@ func Serve(handler http.Handler) error {
}
type response struct {
- req *http.Request
- header http.Header
- bufw *bufio.Writer
- headerSent bool
+ req *http.Request
+ header http.Header
+ code int
+ wroteHeader bool
+ wroteCGIHeader bool
+ bufw *bufio.Writer
}
func (r *response) Flush() {
@@ -178,26 +180,38 @@ func (r *response) Header() http.Header {
}
func (r *response) Write(p []byte) (n int, err error) {
- if !r.headerSent {
+ if !r.wroteHeader {
r.WriteHeader(http.StatusOK)
}
+ if !r.wroteCGIHeader {
+ r.writeCGIHeader(p)
+ }
return r.bufw.Write(p)
}
func (r *response) WriteHeader(code int) {
- if r.headerSent {
+ if r.wroteHeader {
// Note: explicitly using Stderr, as Stdout is our HTTP output.
fmt.Fprintf(os.Stderr, "CGI attempted to write header twice on request for %s", r.req.URL)
return
}
- r.headerSent = true
- fmt.Fprintf(r.bufw, "Status: %d %s\r\n", code, http.StatusText(code))
+ r.wroteHeader = true
+ r.code = code
+}
- // Set a default Content-Type
+// writeCGIHeader finalizes the header sent to the client and writes it to the output.
+// p is not written by writeHeader, but is the first chunk of the body
+// that will be written. It is sniffed for a Content-Type if none is
+// set explicitly.
+func (r *response) writeCGIHeader(p []byte) {
+ if r.wroteCGIHeader {
+ return
+ }
+ r.wroteCGIHeader = true
+ fmt.Fprintf(r.bufw, "Status: %d %s\r\n", r.code, http.StatusText(r.code))
if _, hasType := r.header["Content-Type"]; !hasType {
- r.header.Add("Content-Type", "text/html; charset=utf-8")
+ r.header.Set("Content-Type", http.DetectContentType(p))
}
-
r.header.Write(r.bufw)
r.bufw.WriteString("\r\n")
r.bufw.Flush()
diff --git a/src/net/http/cgi/child_test.go b/src/net/http/cgi/child_test.go
index 14e0af475f..f6ecb6eb80 100644
--- a/src/net/http/cgi/child_test.go
+++ b/src/net/http/cgi/child_test.go
@@ -7,6 +7,11 @@
package cgi
import (
+ "bufio"
+ "bytes"
+ "net/http"
+ "net/http/httptest"
+ "strings"
"testing"
)
@@ -148,3 +153,67 @@ func TestRequestWithoutRemotePort(t *testing.T) {
t.Errorf("RemoteAddr: got %q; want %q", g, e)
}
}
+
+type countingWriter int
+
+func (c *countingWriter) Write(p []byte) (int, error) {
+ *c += countingWriter(len(p))
+ return len(p), nil
+}
+func (c *countingWriter) WriteString(p string) (int, error) {
+ *c += countingWriter(len(p))
+ return len(p), nil
+}
+
+func TestResponse(t *testing.T) {
+ var tests = []struct {
+ name string
+ body string
+ wantCT string
+ }{
+ {
+ name: "no body",
+ wantCT: "text/plain; charset=utf-8",
+ },
+ {
+ name: "html",
+ body: "<html><head><title>test page</title></head><body>This is a body</body></html>",
+ wantCT: "text/html; charset=utf-8",
+ },
+ {
+ name: "text",
+ body: strings.Repeat("gopher", 86),
+ wantCT: "text/plain; charset=utf-8",
+ },
+ {
+ name: "jpg",
+ body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024),
+ wantCT: "image/jpeg",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ var buf bytes.Buffer
+ resp := response{
+ req: httptest.NewRequest("GET", "/", nil),
+ header: http.Header{},
+ bufw: bufio.NewWriter(&buf),
+ }
+ n, err := resp.Write([]byte(tt.body))
+ if err != nil {
+ t.Errorf("Write: unexpected %v", err)
+ }
+ if want := len(tt.body); n != want {
+ t.Errorf("reported short Write: got %v want %v", n, want)
+ }
+ resp.writeCGIHeader(nil)
+ resp.Flush()
+ if got := resp.Header().Get("Content-Type"); got != tt.wantCT {
+ t.Errorf("wrong content-type: got %q, want %q", got, tt.wantCT)
+ }
+ if !bytes.HasSuffix(buf.Bytes(), []byte(tt.body)) {
+ t.Errorf("body was not correctly written")
+ }
+ })
+ }
+}
diff --git a/src/net/http/cgi/integration_test.go b/src/net/http/cgi/integration_test.go
index 32d59c09a3..295c3b82d4 100644
--- a/src/net/http/cgi/integration_test.go
+++ b/src/net/http/cgi/integration_test.go
@@ -16,7 +16,9 @@ import (
"io"
"net/http"
"net/http/httptest"
+ "net/url"
"os"
+ "strings"
"testing"
"time"
)
@@ -52,7 +54,7 @@ func TestHostingOurselves(t *testing.T) {
}
replay := runCgiTest(t, h, "GET /test.go?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
- if expected, got := "text/html; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
+ if expected, got := "text/plain; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
t.Errorf("got a Content-Type of %q; expected %q", got, expected)
}
if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
@@ -152,6 +154,51 @@ func TestChildOnlyHeaders(t *testing.T) {
}
}
+func TestChildContentType(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ h := &Handler{
+ Path: os.Args[0],
+ Root: "/test.go",
+ Args: []string{"-test.run=TestBeChildCGIProcess"},
+ }
+ var tests = []struct {
+ name string
+ body string
+ wantCT string
+ }{
+ {
+ name: "no body",
+ wantCT: "text/plain; charset=utf-8",
+ },
+ {
+ name: "html",
+ body: "<html><head><title>test page</title></head><body>This is a body</body></html>",
+ wantCT: "text/html; charset=utf-8",
+ },
+ {
+ name: "text",
+ body: strings.Repeat("gopher", 86),
+ wantCT: "text/plain; charset=utf-8",
+ },
+ {
+ name: "jpg",
+ body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024),
+ wantCT: "image/jpeg",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ expectedMap := map[string]string{"_body": tt.body}
+ req := fmt.Sprintf("GET /test.go?exact-body=%s HTTP/1.0\nHost: example.com\n\n", url.QueryEscape(tt.body))
+ replay := runCgiTest(t, h, req, expectedMap)
+ if got := replay.Header().Get("Content-Type"); got != tt.wantCT {
+ t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT)
+ }
+ })
+ }
+}
+
// golang.org/issue/7198
func Test500WithNoHeaders(t *testing.T) { want500Test(t, "/immediate-disconnect") }
func Test500WithNoContentType(t *testing.T) { want500Test(t, "/no-content-type") }
@@ -203,6 +250,10 @@ func TestBeChildCGIProcess(t *testing.T) {
if req.FormValue("no-body") == "1" {
return
}
+ if eb, ok := req.Form["exact-body"]; ok {
+ io.WriteString(rw, eb[0])
+ return
+ }
if req.FormValue("write-forever") == "1" {
io.Copy(rw, neverEnding('a'))
for {
diff --git a/src/net/http/fcgi/child.go b/src/net/http/fcgi/child.go
index 30a6b2ce2d..a31273b3ec 100644
--- a/src/net/http/fcgi/child.go
+++ b/src/net/http/fcgi/child.go
@@ -74,10 +74,12 @@ func (r *request) parseParams() {
// response implements http.ResponseWriter.
type response struct {
- req *request
- header http.Header
- w *bufWriter
- wroteHeader bool
+ req *request
+ header http.Header
+ code int
+ wroteHeader bool
+ wroteCGIHeader bool
+ w *bufWriter
}
func newResponse(c *child, req *request) *response {
@@ -92,11 +94,14 @@ func (r *response) Header() http.Header {
return r.header
}
-func (r *response) Write(data []byte) (int, error) {
+func (r *response) Write(p []byte) (n int, err error) {
if !r.wroteHeader {
r.WriteHeader(http.StatusOK)
}
- return r.w.Write(data)
+ if !r.wroteCGIHeader {
+ r.writeCGIHeader(p)
+ }
+ return r.w.Write(p)
}
func (r *response) WriteHeader(code int) {
@@ -104,22 +109,34 @@ func (r *response) WriteHeader(code int) {
return
}
r.wroteHeader = true
+ r.code = code
if code == http.StatusNotModified {
// Must not have body.
r.header.Del("Content-Type")
r.header.Del("Content-Length")
r.header.Del("Transfer-Encoding")
- } else if r.header.Get("Content-Type") == "" {
- r.header.Set("Content-Type", "text/html; charset=utf-8")
}
-
if r.header.Get("Date") == "" {
r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
}
+}
- fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code))
+// writeCGIHeader finalizes the header sent to the client and writes it to the output.
+// p is not written by writeHeader, but is the first chunk of the body
+// that will be written. It is sniffed for a Content-Type if none is
+// set explicitly.
+func (r *response) writeCGIHeader(p []byte) {
+ if r.wroteCGIHeader {
+ return
+ }
+ r.wroteCGIHeader = true
+ fmt.Fprintf(r.w, "Status: %d %s\r\n", r.code, http.StatusText(r.code))
+ if _, hasType := r.header["Content-Type"]; r.code != http.StatusNotModified && !hasType {
+ r.header.Set("Content-Type", http.DetectContentType(p))
+ }
r.header.Write(r.w)
r.w.WriteString("\r\n")
+ r.w.Flush()
}
func (r *response) Flush() {
@@ -290,6 +307,8 @@ func (c *child) serveRequest(req *request, body io.ReadCloser) {
httpReq = httpReq.WithContext(envVarCtx)
c.handler.ServeHTTP(r, httpReq)
}
+ // Make sure we serve something even if nothing was written to r
+ r.Write(nil)
r.Close()
c.mu.Lock()
delete(c.requests, req.reqId)
diff --git a/src/net/http/fcgi/fcgi_test.go b/src/net/http/fcgi/fcgi_test.go
index e9d2b34023..59246c26cc 100644
--- a/src/net/http/fcgi/fcgi_test.go
+++ b/src/net/http/fcgi/fcgi_test.go
@@ -10,6 +10,7 @@ import (
"io"
"io/ioutil"
"net/http"
+ "strings"
"testing"
)
@@ -344,3 +345,55 @@ func TestChildServeReadsEnvVars(t *testing.T) {
<-done
}
}
+
+func TestResponseWriterSniffsContentType(t *testing.T) {
+ t.Skip("this test is flaky, see Issue 41167")
+ var tests = []struct {
+ name string
+ body string
+ wantCT string
+ }{
+ {
+ name: "no body",
+ wantCT: "text/plain; charset=utf-8",
+ },
+ {
+ name: "html",
+ body: "<html><head><title>test page</title></head><body>This is a body</body></html>",
+ wantCT: "text/html; charset=utf-8",
+ },
+ {
+ name: "text",
+ body: strings.Repeat("gopher", 86),
+ wantCT: "text/plain; charset=utf-8",
+ },
+ {
+ name: "jpg",
+ body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024),
+ wantCT: "image/jpeg",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ input := make([]byte, len(streamFullRequestStdin))
+ copy(input, streamFullRequestStdin)
+ rc := nopWriteCloser{bytes.NewBuffer(input)}
+ done := make(chan bool)
+ var resp *response
+ c := newChild(rc, http.HandlerFunc(func(
+ w http.ResponseWriter,
+ r *http.Request,
+ ) {
+ io.WriteString(w, tt.body)
+ resp = w.(*response)
+ done <- true
+ }))
+ defer c.cleanUp()
+ go c.serve()
+ <-done
+ if got := resp.Header().Get("Content-Type"); got != tt.wantCT {
+ t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT)
+ }
+ })
+ }
+}
diff --git a/src/net/mail/message.go b/src/net/mail/message.go
index 6833cfaec1..09fb794005 100644
--- a/src/net/mail/message.go
+++ b/src/net/mail/message.go
@@ -279,9 +279,6 @@ func (p *addrParser) parseAddressList() ([]*Address, error) {
if p.consume(',') {
continue
}
- if p.empty() {
- break
- }
addrs, err := p.parseAddress(true)
if err != nil {
@@ -295,9 +292,17 @@ func (p *addrParser) parseAddressList() ([]*Address, error) {
if p.empty() {
break
}
- if !p.consume(',') {
+ if p.peek() != ',' {
return nil, errors.New("mail: expected comma")
}
+
+ // Skip empty entries for obs-addr-list.
+ for p.consume(',') {
+ p.skipSpace()
+ }
+ if p.empty() {
+ break
+ }
}
return list, nil
}
diff --git a/src/net/mail/message_test.go b/src/net/mail/message_test.go
index 75db767547..67e3643aeb 100644
--- a/src/net/mail/message_test.go
+++ b/src/net/mail/message_test.go
@@ -446,6 +446,19 @@ func TestAddressParsing(t *testing.T) {
},
},
{
+ ` , joe@where.test,,John <jdoe@one.test>,,`,
+ []*Address{
+ {
+ Name: "",
+ Address: "joe@where.test",
+ },
+ {
+ Name: "John",
+ Address: "jdoe@one.test",
+ },
+ },
+ },
+ {
`Group1: <addr1@example.com>;, Group 2: addr2@example.com;, John <addr3@example.com>`,
[]*Address{
{
@@ -1067,3 +1080,22 @@ func TestAddressFormattingAndParsing(t *testing.T) {
}
}
}
+
+func TestEmptyAddress(t *testing.T) {
+ parsed, err := ParseAddress("")
+ if parsed != nil || err == nil {
+ t.Errorf(`ParseAddress("") = %v, %v, want nil, error`, parsed, err)
+ }
+ list, err := ParseAddressList("")
+ if len(list) > 0 || err == nil {
+ t.Errorf(`ParseAddressList("") = %v, %v, want nil, error`, list, err)
+ }
+ list, err = ParseAddressList(",")
+ if len(list) > 0 || err == nil {
+ t.Errorf(`ParseAddressList("") = %v, %v, want nil, error`, list, err)
+ }
+ list, err = ParseAddressList("a@b c@d")
+ if len(list) > 0 || err == nil {
+ t.Errorf(`ParseAddressList("") = %v, %v, want nil, error`, list, err)
+ }
+}
diff --git a/src/runtime/asm_ppc64x.s b/src/runtime/asm_ppc64x.s
index 11d2f2f51a..23387a2165 100644
--- a/src/runtime/asm_ppc64x.s
+++ b/src/runtime/asm_ppc64x.s
@@ -916,23 +916,23 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1
// - R20 is the destination of the write
// - R21 is the value being written at R20.
// It clobbers condition codes.
-// It does not clobber R0 through R15,
+// It does not clobber R0 through R17 (except special registers),
// but may clobber any other register, *including* R31.
TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$112
// The standard prologue clobbers R31.
- // We use R16 and R17 as scratch registers.
- MOVD g_m(g), R16
- MOVD m_p(R16), R16
- MOVD (p_wbBuf+wbBuf_next)(R16), R17
+ // We use R18 and R19 as scratch registers.
+ MOVD g_m(g), R18
+ MOVD m_p(R18), R18
+ MOVD (p_wbBuf+wbBuf_next)(R18), R19
// Increment wbBuf.next position.
- ADD $16, R17
- MOVD R17, (p_wbBuf+wbBuf_next)(R16)
- MOVD (p_wbBuf+wbBuf_end)(R16), R16
- CMP R16, R17
+ ADD $16, R19
+ MOVD R19, (p_wbBuf+wbBuf_next)(R18)
+ MOVD (p_wbBuf+wbBuf_end)(R18), R18
+ CMP R18, R19
// Record the write.
- MOVD R21, -16(R17) // Record value
- MOVD (R20), R16 // TODO: This turns bad writes into bad reads.
- MOVD R16, -8(R17) // Record *slot
+ MOVD R21, -16(R19) // Record value
+ MOVD (R20), R18 // TODO: This turns bad writes into bad reads.
+ MOVD R18, -8(R19) // Record *slot
// Is the buffer full? (flags set in CMP above)
BEQ flush
ret:
@@ -956,11 +956,12 @@ flush:
MOVD R8, (FIXED_FRAME+56)(R1)
MOVD R9, (FIXED_FRAME+64)(R1)
MOVD R10, (FIXED_FRAME+72)(R1)
- MOVD R11, (FIXED_FRAME+80)(R1)
- MOVD R12, (FIXED_FRAME+88)(R1)
+ // R11, R12 may be clobbered by external-linker-inserted trampoline
// R13 is REGTLS
- MOVD R14, (FIXED_FRAME+96)(R1)
- MOVD R15, (FIXED_FRAME+104)(R1)
+ MOVD R14, (FIXED_FRAME+80)(R1)
+ MOVD R15, (FIXED_FRAME+88)(R1)
+ MOVD R16, (FIXED_FRAME+96)(R1)
+ MOVD R17, (FIXED_FRAME+104)(R1)
// This takes arguments R20 and R21.
CALL runtime·wbBufFlush(SB)
@@ -975,10 +976,10 @@ flush:
MOVD (FIXED_FRAME+56)(R1), R8
MOVD (FIXED_FRAME+64)(R1), R9
MOVD (FIXED_FRAME+72)(R1), R10
- MOVD (FIXED_FRAME+80)(R1), R11
- MOVD (FIXED_FRAME+88)(R1), R12
- MOVD (FIXED_FRAME+96)(R1), R14
- MOVD (FIXED_FRAME+104)(R1), R15
+ MOVD (FIXED_FRAME+80)(R1), R14
+ MOVD (FIXED_FRAME+88)(R1), R15
+ MOVD (FIXED_FRAME+96)(R1), R16
+ MOVD (FIXED_FRAME+104)(R1), R17
JMP ret
// Note: these functions use a special calling convention to save generated code space.
diff --git a/src/runtime/checkptr_test.go b/src/runtime/checkptr_test.go
index 8ab8a4937c..194cc1243a 100644
--- a/src/runtime/checkptr_test.go
+++ b/src/runtime/checkptr_test.go
@@ -27,6 +27,7 @@ func TestCheckPtr(t *testing.T) {
{"CheckPtrAlignmentPtr", "fatal error: checkptr: misaligned pointer conversion\n"},
{"CheckPtrAlignmentNoPtr", ""},
{"CheckPtrArithmetic", "fatal error: checkptr: pointer arithmetic result points to invalid allocation\n"},
+ {"CheckPtrArithmetic2", "fatal error: checkptr: pointer arithmetic result points to invalid allocation\n"},
{"CheckPtrSize", "fatal error: checkptr: converted pointer straddles multiple allocations\n"},
{"CheckPtrSmall", "fatal error: checkptr: pointer arithmetic computed bad pointer value\n"},
}
diff --git a/src/runtime/lockrank_off.go b/src/runtime/lockrank_off.go
index 425ca8dd93..32378a9627 100644
--- a/src/runtime/lockrank_off.go
+++ b/src/runtime/lockrank_off.go
@@ -18,19 +18,29 @@ func getLockRank(l *mutex) lockRank {
return 0
}
+// The following functions may be called in nosplit context.
+// Nosplit is not strictly required for lockWithRank, unlockWithRank
+// and lockWithRankMayAcquire, but these nosplit annotations must
+// be kept consistent with the equivalent functions in lockrank_on.go.
+
+//go:nosplit
func lockWithRank(l *mutex, rank lockRank) {
lock2(l)
}
+//go:nosplit
func acquireLockRank(rank lockRank) {
}
+//go:nosplit
func unlockWithRank(l *mutex) {
unlock2(l)
}
+//go:nosplit
func releaseLockRank(rank lockRank) {
}
+//go:nosplit
func lockWithRankMayAcquire(l *mutex, rank lockRank) {
}
diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
index a584ada702..769197db46 100644
--- a/src/runtime/os_windows.go
+++ b/src/runtime/os_windows.go
@@ -36,7 +36,10 @@ const (
//go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll"
//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._OpenProcess OpenProcess%3 "kernel32.dll"
//go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll"
+//go:cgo_import_dynamic runtime._ProcessIdToSessionId ProcessIdToSessionId%2 "kernel32.dll"
+//go:cgo_import_dynamic runtime._QueryFullProcessImageNameA QueryFullProcessImageNameA%4 "kernel32.dll"
//go:cgo_import_dynamic runtime._ResumeThread ResumeThread%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._SetConsoleCtrlHandler SetConsoleCtrlHandler%2 "kernel32.dll"
//go:cgo_import_dynamic runtime._SetErrorMode SetErrorMode%1 "kernel32.dll"
@@ -84,7 +87,10 @@ var (
_SetThreadContext,
_LoadLibraryW,
_LoadLibraryA,
+ _OpenProcess,
_PostQueuedCompletionStatus,
+ _ProcessIdToSessionId,
+ _QueryFullProcessImageNameA,
_QueryPerformanceCounter,
_QueryPerformanceFrequency,
_ResumeThread,
@@ -128,7 +134,8 @@ var (
// Load ntdll.dll manually during startup, otherwise Mingw
// links wrong printf function to cgo executable (see issue
// 12030 for details).
- _NtWaitForSingleObject stdFunction
+ _NtWaitForSingleObject stdFunction
+ _NtQueryInformationProcess stdFunction
// These are from non-kernel32.dll, so we prefer to LoadLibraryEx them.
_timeBeginPeriod,
@@ -255,6 +262,7 @@ func loadOptionalSyscalls() {
throw("ntdll.dll not found")
}
_NtWaitForSingleObject = windowsFindfunc(n32, []byte("NtWaitForSingleObject\000"))
+ _NtQueryInformationProcess = windowsFindfunc(n32, []byte("NtQueryInformationProcess\000"))
if GOARCH == "arm" {
_QueryPerformanceCounter = windowsFindfunc(k32, []byte("QueryPerformanceCounter\000"))
@@ -995,6 +1003,63 @@ func usleep(us uint32) {
onosstack(usleep2Addr, 10*us)
}
+// isWindowsService returns whether the process is currently executing as a
+// Windows service. The below technique looks a bit hairy, but it's actually
+// exactly what the .NET framework does for the similarly named function:
+// https://github.com/dotnet/extensions/blob/f4066026ca06984b07e90e61a6390ac38152ba93/src/Hosting/WindowsServices/src/WindowsServiceHelpers.cs#L26-L31
+// Specifically, it looks up whether the parent process has session ID zero
+// and is called "services".
+func isWindowsService() bool {
+ const (
+ _CURRENT_PROCESS = ^uintptr(0)
+ _PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
+ )
+ // pbi is a PROCESS_BASIC_INFORMATION struct, where we just care about
+ // the 6th pointer inside of it, which contains the pid of the process
+ // parent:
+ // https://github.com/wine-mirror/wine/blob/42cb7d2ad1caba08de235e6319b9967296b5d554/include/winternl.h#L1294
+ var pbi [6]uintptr
+ var pbiLen uint32
+ err := stdcall5(_NtQueryInformationProcess, _CURRENT_PROCESS, 0, uintptr(unsafe.Pointer(&pbi[0])), uintptr(unsafe.Sizeof(pbi)), uintptr(unsafe.Pointer(&pbiLen)))
+ if err != 0 {
+ return false
+ }
+ var psid uint32
+ err = stdcall2(_ProcessIdToSessionId, pbi[5], uintptr(unsafe.Pointer(&psid)))
+ if err == 0 || psid != 0 {
+ return false
+ }
+ pproc := stdcall3(_OpenProcess, _PROCESS_QUERY_LIMITED_INFORMATION, 0, pbi[5])
+ if pproc == 0 {
+ return false
+ }
+ defer stdcall1(_CloseHandle, pproc)
+ // exeName gets the path to the executable image of the parent process
+ var exeName [261]byte
+ exeNameLen := uint32(len(exeName) - 1)
+ err = stdcall4(_QueryFullProcessImageNameA, pproc, 0, uintptr(unsafe.Pointer(&exeName[0])), uintptr(unsafe.Pointer(&exeNameLen)))
+ if err == 0 || exeNameLen == 0 {
+ return false
+ }
+ servicesLower := "services.exe"
+ servicesUpper := "SERVICES.EXE"
+ i := int(exeNameLen) - 1
+ j := len(servicesLower) - 1
+ if i < j {
+ return false
+ }
+ for {
+ if j == -1 {
+ return i == -1 || exeName[i] == '\\'
+ }
+ if exeName[i] != servicesLower[j] && exeName[i] != servicesUpper[j] {
+ return false
+ }
+ i--
+ j--
+ }
+}
+
func ctrlhandler1(_type uint32) uint32 {
var s uint32
@@ -1010,9 +1075,9 @@ func ctrlhandler1(_type uint32) uint32 {
if sigsend(s) {
return 1
}
- if !islibrary && !isarchive {
- // Only exit the program if we don't have a DLL.
- // See https://golang.org/issues/35965.
+ if !islibrary && !isarchive && !isWindowsService() {
+ // Only exit the program if we don't have a DLL or service.
+ // See https://golang.org/issues/35965 and https://golang.org/issues/40167
exit(2) // SIGINT, SIGTERM, etc
}
return 0
diff --git a/src/runtime/testdata/testprog/checkptr.go b/src/runtime/testdata/testprog/checkptr.go
index 45e6fb1aa5..e0a2794f4c 100644
--- a/src/runtime/testdata/testprog/checkptr.go
+++ b/src/runtime/testdata/testprog/checkptr.go
@@ -10,6 +10,7 @@ func init() {
register("CheckPtrAlignmentNoPtr", CheckPtrAlignmentNoPtr)
register("CheckPtrAlignmentPtr", CheckPtrAlignmentPtr)
register("CheckPtrArithmetic", CheckPtrArithmetic)
+ register("CheckPtrArithmetic2", CheckPtrArithmetic2)
register("CheckPtrSize", CheckPtrSize)
register("CheckPtrSmall", CheckPtrSmall)
}
@@ -32,6 +33,13 @@ func CheckPtrArithmetic() {
sink2 = (*int)(unsafe.Pointer(i))
}
+func CheckPtrArithmetic2() {
+ var x [2]int64
+ p := unsafe.Pointer(&x[1])
+ var one uintptr = 1
+ sink2 = unsafe.Pointer(uintptr(p) & ^one)
+}
+
func CheckPtrSize() {
p := new(int64)
sink2 = p
diff --git a/src/sync/map.go b/src/sync/map.go
index a61e2ebdd6..9ad25353ff 100644
--- a/src/sync/map.go
+++ b/src/sync/map.go
@@ -274,6 +274,7 @@ func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool) {
e, ok = read.m[key]
if !ok && read.amended {
e, ok = m.dirty[key]
+ delete(m.dirty, key)
// Regardless of whether the entry was present, record a miss: this key
// will take the slow path until the dirty map is promoted to the read
// map.
diff --git a/src/sync/map_test.go b/src/sync/map_test.go
index 4ae989a6d5..7f163caa5c 100644
--- a/src/sync/map_test.go
+++ b/src/sync/map_test.go
@@ -9,6 +9,7 @@ import (
"reflect"
"runtime"
"sync"
+ "sync/atomic"
"testing"
"testing/quick"
)
@@ -171,3 +172,26 @@ func TestConcurrentRange(t *testing.T) {
}
}
}
+
+func TestIssue40999(t *testing.T) {
+ var m sync.Map
+
+ // Since the miss-counting in missLocked (via Delete)
+ // compares the miss count with len(m.dirty),
+ // add an initial entry to bias len(m.dirty) above the miss count.
+ m.Store(nil, struct{}{})
+
+ var finalized uint32
+
+ // Set finalizers that count for collected keys. A non-zero count
+ // indicates that keys have not been leaked.
+ for atomic.LoadUint32(&finalized) == 0 {
+ p := new(int)
+ runtime.SetFinalizer(p, func(*int) {
+ atomic.AddUint32(&finalized, 1)
+ })
+ m.Store(p, struct{}{})
+ m.Delete(p)
+ runtime.GC()
+ }
+}
diff --git a/src/testing/testing.go b/src/testing/testing.go
index 061142b9ab..bf83df8863 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -357,10 +357,19 @@ func (p *testPrinter) Fprint(w io.Writer, testName, out string) {
defer p.lastNameMu.Unlock()
if !p.chatty ||
- strings.HasPrefix(out, "--- PASS") ||
- strings.HasPrefix(out, "--- FAIL") ||
- strings.HasPrefix(out, "=== CONT") ||
- strings.HasPrefix(out, "=== RUN") {
+ strings.HasPrefix(out, "--- PASS: ") ||
+ strings.HasPrefix(out, "--- FAIL: ") ||
+ strings.HasPrefix(out, "--- SKIP: ") ||
+ strings.HasPrefix(out, "=== RUN ") ||
+ strings.HasPrefix(out, "=== CONT ") ||
+ strings.HasPrefix(out, "=== PAUSE ") {
+ // If we're buffering test output (!p.chatty), we don't really care which
+ // test is emitting which line so long as they are serialized.
+ //
+ // If the message already implies an association with a specific new test,
+ // we don't need to check what the old test name was or log an extra CONT
+ // line for it. (We're updating it anyway, and the current message already
+ // includes the test name.)
p.lastName = testName
fmt.Fprint(w, out)
return
@@ -851,11 +860,15 @@ func (c *common) Cleanup(f func()) {
c.cleanup = func() {
if oldCleanup != nil {
defer func() {
+ c.mu.Lock()
c.cleanupPc = oldCleanupPc
+ c.mu.Unlock()
oldCleanup()
}()
}
+ c.mu.Lock()
c.cleanupName = callerName(0)
+ c.mu.Unlock()
f()
}
var pc [maxStackLen]uintptr
@@ -976,7 +989,13 @@ func (t *T) Parallel() {
for ; root.parent != nil; root = root.parent {
}
root.mu.Lock()
- fmt.Fprintf(root.w, "=== PAUSE %s\n", t.name)
+ // Unfortunately, even though PAUSE indicates that the named test is *no
+ // longer* running, cmd/test2json interprets it as changing the active test
+ // for the purpose of log parsing. We could fix cmd/test2json, but that
+ // won't fix existing deployments of third-party tools that already shell
+ // out to older builds of cmd/test2json — so merely fixing cmd/test2json
+ // isn't enough for now.
+ printer.Fprint(root.w, t.name, fmt.Sprintf("=== PAUSE %s\n", t.name))
root.mu.Unlock()
}
diff --git a/test/fixedbugs/issue40746.go b/test/fixedbugs/issue40746.go
new file mode 100644
index 0000000000..235282fd90
--- /dev/null
+++ b/test/fixedbugs/issue40746.go
@@ -0,0 +1,19 @@
+// compile
+
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+func f(x byte, b bool) byte {
+ var c byte
+ if b {
+ c = 1
+ }
+
+ if int8(c) < 0 {
+ x++
+ }
+ return x
+}
diff --git a/test/fixedbugs/issue40917.go b/test/fixedbugs/issue40917.go
new file mode 100644
index 0000000000..2128be5eca
--- /dev/null
+++ b/test/fixedbugs/issue40917.go
@@ -0,0 +1,23 @@
+// run -gcflags=-d=checkptr
+
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "unsafe"
+
+func main() {
+ var x [2]uint64
+ a := unsafe.Pointer(&x[1])
+
+ b := a
+ b = unsafe.Pointer(uintptr(b) + 2)
+ b = unsafe.Pointer(uintptr(b) - 1)
+ b = unsafe.Pointer(uintptr(b) &^ 1)
+
+ if a != b {
+ panic("pointer arithmetic failed")
+ }
+}