mirror of
https://github.com/golang/go.git
synced 2026-01-30 23:52:05 +03:00
Compare commits
28 Commits
dev.inline
...
go1.19.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a4127bccc | ||
|
|
9cfe4e258b | ||
|
|
86e9e0ea87 | ||
|
|
0bba4d2fe6 | ||
|
|
823e5d8afa | ||
|
|
11033eac90 | ||
|
|
4580d6dc6d | ||
|
|
62aa93010d | ||
|
|
15b234b830 | ||
|
|
4da9d6aa10 | ||
|
|
4b1c16cc45 | ||
|
|
2553a09e31 | ||
|
|
2833550891 | ||
|
|
d2bcb22ce0 | ||
|
|
d39fd4f2b7 | ||
|
|
f1b38e8b41 | ||
|
|
2b59f4dd19 | ||
|
|
2375ef8df6 | ||
|
|
16c2b3615a | ||
|
|
3c200d6c81 | ||
|
|
43456202a1 | ||
|
|
56ec1163e3 | ||
|
|
ec7bf2007e | ||
|
|
46ab46c7c5 | ||
|
|
ad672e7ce1 | ||
|
|
392548f147 | ||
|
|
bac4eb53d6 | ||
|
|
18033a0b56 |
@@ -1 +1,2 @@
|
||||
branch: master
|
||||
branch: release-branch.go1.19
|
||||
parent-branch: master
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -519,38 +520,13 @@ func TestEarlySignalHandler(t *testing.T) {
|
||||
|
||||
func TestSignalForwarding(t *testing.T) {
|
||||
checkSignalForwardingTest(t)
|
||||
buildSignalForwardingTest(t)
|
||||
|
||||
if !testWork {
|
||||
defer func() {
|
||||
os.Remove("libgo2.a")
|
||||
os.Remove("libgo2.h")
|
||||
os.Remove("testp" + exeSuffix)
|
||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||
}()
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkLineComments(t, "libgo2.h")
|
||||
checkArchive(t, "libgo2.a")
|
||||
|
||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
||||
if runtime.Compiler == "gccgo" {
|
||||
ccArgs = append(ccArgs, "-lgo")
|
||||
}
|
||||
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cmd = exec.Command(bin[0], append(bin[1:], "1")...)
|
||||
cmd := exec.Command(bin[0], append(bin[1:], "1")...)
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
t.Logf("%v\n%s", cmd.Args, out)
|
||||
expectSignal(t, err, syscall.SIGSEGV)
|
||||
expectSignal(t, err, syscall.SIGSEGV, 0)
|
||||
|
||||
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
|
||||
if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
|
||||
@@ -561,7 +537,7 @@ func TestSignalForwarding(t *testing.T) {
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s", out)
|
||||
}
|
||||
expectSignal(t, err, syscall.SIGPIPE)
|
||||
expectSignal(t, err, syscall.SIGPIPE, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,32 +548,7 @@ func TestSignalForwardingExternal(t *testing.T) {
|
||||
t.Skipf("skipping on %s/%s: runtime does not permit SI_USER SIGSEGV", GOOS, GOARCH)
|
||||
}
|
||||
checkSignalForwardingTest(t)
|
||||
|
||||
if !testWork {
|
||||
defer func() {
|
||||
os.Remove("libgo2.a")
|
||||
os.Remove("libgo2.h")
|
||||
os.Remove("testp" + exeSuffix)
|
||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||
}()
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkLineComments(t, "libgo2.h")
|
||||
checkArchive(t, "libgo2.a")
|
||||
|
||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
||||
if runtime.Compiler == "gccgo" {
|
||||
ccArgs = append(ccArgs, "-lgo")
|
||||
}
|
||||
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
buildSignalForwardingTest(t)
|
||||
|
||||
// We want to send the process a signal and see if it dies.
|
||||
// Normally the signal goes to the C thread, the Go signal
|
||||
@@ -610,42 +561,27 @@ func TestSignalForwardingExternal(t *testing.T) {
|
||||
// fail.
|
||||
const tries = 20
|
||||
for i := 0; i < tries; i++ {
|
||||
cmd = exec.Command(bin[0], append(bin[1:], "2")...)
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer stderr.Close()
|
||||
|
||||
r := bufio.NewReader(stderr)
|
||||
|
||||
err = cmd.Start()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Wait for trigger to ensure that the process is started.
|
||||
ok, err := r.ReadString('\n')
|
||||
|
||||
// Verify trigger.
|
||||
if err != nil || ok != "OK\n" {
|
||||
t.Fatalf("Did not receive OK signal")
|
||||
}
|
||||
|
||||
// Give the program a chance to enter the sleep function.
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
cmd.Process.Signal(syscall.SIGSEGV)
|
||||
|
||||
err = cmd.Wait()
|
||||
|
||||
err := runSignalForwardingTest(t, "2")
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if expectSignal(t, err, syscall.SIGSEGV) {
|
||||
// If the signal is delivered to a C thread, as expected,
|
||||
// the Go signal handler will disable itself and re-raise
|
||||
// the signal, causing the program to die with SIGSEGV.
|
||||
//
|
||||
// It is also possible that the signal will be
|
||||
// delivered to a Go thread, such as a GC thread.
|
||||
// Currently when the Go runtime sees that a SIGSEGV was
|
||||
// sent from a different program, it first tries to send
|
||||
// the signal to the os/signal API. If nothing is looking
|
||||
// for (or explicitly ignoring) SIGSEGV, then it crashes.
|
||||
// Because the Go runtime is invoked via a c-archive,
|
||||
// it treats this as GOTRACEBACK=crash, meaning that it
|
||||
// dumps a stack trace for all goroutines, which it does
|
||||
// by raising SIGQUIT. The effect is that we will see the
|
||||
// program die with SIGQUIT in that case, not SIGSEGV.
|
||||
if expectSignal(t, err, syscall.SIGSEGV, syscall.SIGQUIT) {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -653,6 +589,23 @@ func TestSignalForwardingExternal(t *testing.T) {
|
||||
t.Errorf("program succeeded unexpectedly %d times", tries)
|
||||
}
|
||||
|
||||
func TestSignalForwardingGo(t *testing.T) {
|
||||
// This test fails on darwin-amd64 because of the special
|
||||
// handling of user-generated SIGSEGV signals in fixsigcode in
|
||||
// runtime/signal_darwin_amd64.go.
|
||||
if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" {
|
||||
t.Skip("not supported on darwin-amd64")
|
||||
}
|
||||
|
||||
checkSignalForwardingTest(t)
|
||||
buildSignalForwardingTest(t)
|
||||
err := runSignalForwardingTest(t, "4")
|
||||
|
||||
// Occasionally the signal will be delivered to a C thread,
|
||||
// and the program will crash with SIGSEGV.
|
||||
expectSignal(t, err, syscall.SIGQUIT, syscall.SIGSEGV)
|
||||
}
|
||||
|
||||
// checkSignalForwardingTest calls t.Skip if the SignalForwarding test
|
||||
// doesn't work on this platform.
|
||||
func checkSignalForwardingTest(t *testing.T) {
|
||||
@@ -667,18 +620,121 @@ func checkSignalForwardingTest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// buildSignalForwardingTest builds the executable used by the various
|
||||
// signal forwarding tests.
|
||||
func buildSignalForwardingTest(t *testing.T) {
|
||||
if !testWork {
|
||||
t.Cleanup(func() {
|
||||
os.Remove("libgo2.a")
|
||||
os.Remove("libgo2.h")
|
||||
os.Remove("testp" + exeSuffix)
|
||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||
})
|
||||
}
|
||||
|
||||
t.Log("go build -buildmode=c-archive -o libgo2.a ./libgo2")
|
||||
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s", out)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkLineComments(t, "libgo2.h")
|
||||
checkArchive(t, "libgo2.a")
|
||||
|
||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
||||
if runtime.Compiler == "gccgo" {
|
||||
ccArgs = append(ccArgs, "-lgo")
|
||||
}
|
||||
t.Log(ccArgs)
|
||||
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s", out)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func runSignalForwardingTest(t *testing.T, arg string) error {
|
||||
t.Logf("%v %s", bin, arg)
|
||||
cmd := exec.Command(bin[0], append(bin[1:], arg)...)
|
||||
|
||||
var out strings.Builder
|
||||
cmd.Stdout = &out
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer stderr.Close()
|
||||
|
||||
r := bufio.NewReader(stderr)
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Wait for trigger to ensure that process is started.
|
||||
ok, err := r.ReadString('\n')
|
||||
|
||||
// Verify trigger.
|
||||
if err != nil || ok != "OK\n" {
|
||||
t.Fatal("Did not receive OK signal")
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
var errsb strings.Builder
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
io.Copy(&errsb, r)
|
||||
}()
|
||||
|
||||
// Give the program a chance to enter the function.
|
||||
// If the program doesn't get there the test will still
|
||||
// pass, although it doesn't quite test what we intended.
|
||||
// This is fine as long as the program normally makes it.
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
cmd.Process.Signal(syscall.SIGSEGV)
|
||||
|
||||
err = cmd.Wait()
|
||||
|
||||
s := out.String()
|
||||
if len(s) > 0 {
|
||||
t.Log(s)
|
||||
}
|
||||
wg.Wait()
|
||||
s = errsb.String()
|
||||
if len(s) > 0 {
|
||||
t.Log(s)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// expectSignal checks that err, the exit status of a test program,
|
||||
// shows a failure due to a specific signal. Returns whether we found
|
||||
// the expected signal.
|
||||
func expectSignal(t *testing.T, err error, sig syscall.Signal) bool {
|
||||
// shows a failure due to a specific signal or two. Returns whether we
|
||||
// found an expected signal.
|
||||
func expectSignal(t *testing.T, err error, sig1, sig2 syscall.Signal) bool {
|
||||
t.Helper()
|
||||
if err == nil {
|
||||
t.Error("test program succeeded unexpectedly")
|
||||
} else if ee, ok := err.(*exec.ExitError); !ok {
|
||||
t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
|
||||
} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
|
||||
t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
|
||||
} else if !ws.Signaled() || ws.Signal() != sig {
|
||||
t.Errorf("got %v; expected signal %v", ee, sig)
|
||||
} else if !ws.Signaled() || (ws.Signal() != sig1 && ws.Signal() != sig2) {
|
||||
if sig2 == 0 {
|
||||
t.Errorf("got %q; expected signal %q", ee, sig1)
|
||||
} else {
|
||||
t.Errorf("got %q; expected signal %q or %q", ee, sig1, sig2)
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
@@ -1015,14 +1071,14 @@ func TestCompileWithoutShared(t *testing.T) {
|
||||
binArgs := append(cmdToRun(exe), "1")
|
||||
out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
|
||||
t.Logf("%v\n%s", binArgs, out)
|
||||
expectSignal(t, err, syscall.SIGSEGV)
|
||||
expectSignal(t, err, syscall.SIGSEGV, 0)
|
||||
|
||||
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
|
||||
if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
|
||||
binArgs := append(cmdToRun(exe), "3")
|
||||
out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
|
||||
t.Logf("%v\n%s", binArgs, out)
|
||||
expectSignal(t, err, syscall.SIGPIPE)
|
||||
expectSignal(t, err, syscall.SIGPIPE, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,12 @@ func RunGoroutines() {
|
||||
}
|
||||
}
|
||||
|
||||
// Block blocks the current thread while running Go code.
|
||||
//export Block
|
||||
func Block() {
|
||||
select {}
|
||||
}
|
||||
|
||||
var P *byte
|
||||
|
||||
// TestSEGV makes sure that an invalid address turns into a run-time Go panic.
|
||||
|
||||
13
misc/cgo/testcarchive/testdata/main5.c
vendored
13
misc/cgo/testcarchive/testdata/main5.c
vendored
@@ -29,10 +29,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
verbose = (argc > 2);
|
||||
|
||||
if (verbose) {
|
||||
printf("calling RunGoroutines\n");
|
||||
}
|
||||
|
||||
Noop();
|
||||
|
||||
switch (test) {
|
||||
@@ -90,6 +86,15 @@ int main(int argc, char** argv) {
|
||||
printf("did not receive SIGPIPE\n");
|
||||
return 0;
|
||||
}
|
||||
case 4: {
|
||||
fprintf(stderr, "OK\n");
|
||||
fflush(stderr);
|
||||
|
||||
if (verbose) {
|
||||
printf("calling Block\n");
|
||||
}
|
||||
Block();
|
||||
}
|
||||
default:
|
||||
printf("Unknown test: %d\n", test);
|
||||
return 0;
|
||||
|
||||
@@ -26,6 +26,7 @@ var Syms struct {
|
||||
GCWriteBarrier *obj.LSym
|
||||
Goschedguarded *obj.LSym
|
||||
Growslice *obj.LSym
|
||||
Memmove *obj.LSym
|
||||
Msanread *obj.LSym
|
||||
Msanwrite *obj.LSym
|
||||
Msanmove *obj.LSym
|
||||
|
||||
@@ -25,6 +25,17 @@ func (s *gcSizes) Alignof(T types2.Type) int64 {
|
||||
// is the same as unsafe.Alignof(x[0]), but at least 1."
|
||||
return s.Alignof(t.Elem())
|
||||
case *types2.Struct:
|
||||
if t.NumFields() == 0 && types2.IsSyncAtomicAlign64(T) {
|
||||
// Special case: sync/atomic.align64 is an
|
||||
// empty struct we recognize as a signal that
|
||||
// the struct it contains must be
|
||||
// 64-bit-aligned.
|
||||
//
|
||||
// This logic is equivalent to the logic in
|
||||
// cmd/compile/internal/types/size.go:calcStructOffset
|
||||
return 8
|
||||
}
|
||||
|
||||
// spec: "For a variable x of struct type: unsafe.Alignof(x)
|
||||
// is the largest of the values unsafe.Alignof(x.f) for each
|
||||
// field f of x, but at least 1."
|
||||
|
||||
@@ -334,10 +334,6 @@ func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
||||
} else { // ir.OMETHEXPR or ir.METHVALUE
|
||||
// Method expression T.M where T is a generic type.
|
||||
se := x.(*ir.SelectorExpr)
|
||||
targs := deref(se.X.Type()).RParams()
|
||||
if len(targs) == 0 {
|
||||
panic("bad")
|
||||
}
|
||||
if x.Op() == ir.OMETHVALUE {
|
||||
rcvrValue = se.X
|
||||
}
|
||||
@@ -348,7 +344,8 @@ func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
||||
// of se.Selection, since that will be the type that actually has
|
||||
// the method.
|
||||
recv := deref(se.Selection.Type.Recv().Type)
|
||||
if len(recv.RParams()) == 0 {
|
||||
targs := recv.RParams()
|
||||
if len(targs) == 0 {
|
||||
// The embedded type that actually has the method is not
|
||||
// actually generic, so no need to build a closure.
|
||||
return x
|
||||
@@ -1357,6 +1354,9 @@ func (g *genInst) dictPass(info *instInfo) {
|
||||
}
|
||||
case ir.ODOTTYPE, ir.ODOTTYPE2:
|
||||
dt := m.(*ir.TypeAssertExpr)
|
||||
if dt.Type().IsEmptyInterface() || (dt.Type().IsInterface() && !dt.Type().HasShape()) {
|
||||
break
|
||||
}
|
||||
if !dt.Type().HasShape() && !(dt.X.Type().HasShape() && !dt.X.Type().IsEmptyInterface()) {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1890,14 +1890,14 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
|
||||
// the TOC to the appropriate value for that module. But if it returns
|
||||
// directly to the wrapper's caller, nothing will reset it to the correct
|
||||
// value for that function.
|
||||
var call *ir.CallExpr
|
||||
if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !generic {
|
||||
call := ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
|
||||
call = ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
|
||||
call.Args = ir.ParamNames(fn.Type())
|
||||
call.IsDDD = fn.Type().IsVariadic()
|
||||
fn.Body.Append(ir.NewTailCallStmt(base.Pos, call))
|
||||
} else {
|
||||
fn.SetWrapper(true) // ignore frame for panic+recover matching
|
||||
var call *ir.CallExpr
|
||||
|
||||
if generic && dot.X != nthis {
|
||||
// If there is embedding involved, then we should do the
|
||||
@@ -1982,7 +1982,22 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
|
||||
typecheck.Stmts(fn.Body)
|
||||
|
||||
if AfterGlobalEscapeAnalysis {
|
||||
inline.InlineCalls(fn)
|
||||
// Inlining the method may reveal closures, which require walking all function bodies
|
||||
// to decide whether to capture free variables by value or by ref. So we only do inline
|
||||
// if the method do not contain any closures, otherwise, the escape analysis may make
|
||||
// dead variables resurrected, and causing liveness analysis confused, see issue #53702.
|
||||
var canInline bool
|
||||
switch x := call.X.(type) {
|
||||
case *ir.Name:
|
||||
canInline = len(x.Func.Closures) == 0
|
||||
case *ir.SelectorExpr:
|
||||
if x.Op() == ir.OMETHEXPR {
|
||||
canInline = x.FuncName().Func != nil && len(x.FuncName().Func.Closures) == 0
|
||||
}
|
||||
}
|
||||
if canInline {
|
||||
inline.InlineCalls(fn)
|
||||
}
|
||||
escape.Batch([]*ir.Func{fn}, false)
|
||||
}
|
||||
|
||||
|
||||
@@ -355,7 +355,9 @@ var genericOps = []opData{
|
||||
{name: "Load", argLength: 2}, // Load from arg0. arg1=memory
|
||||
{name: "Dereference", argLength: 2}, // Load from arg0. arg1=memory. Helper op for arg/result passing, result is an otherwise not-SSA-able "value".
|
||||
{name: "Store", argLength: 3, typ: "Mem", aux: "Typ"}, // Store arg1 to arg0. arg2=memory, aux=type. Returns memory.
|
||||
// The source and destination of Move may overlap in some cases. See e.g.
|
||||
// Normally we require that the source and destination of Move do not overlap.
|
||||
// There is an exception when we know all the loads will happen before all
|
||||
// the stores. In that case, overlap is ok. See
|
||||
// memmove inlining in generic.rules. When inlineablememmovesize (in ../rewrite.go)
|
||||
// returns true, we must do all loads before all stores, when lowering Move.
|
||||
// The type of Move is used for the write barrier pass to insert write barriers
|
||||
|
||||
@@ -1362,7 +1362,8 @@ func zeroUpper56Bits(x *Value, depth int) bool {
|
||||
|
||||
// isInlinableMemmove reports whether the given arch performs a Move of the given size
|
||||
// faster than memmove. It will only return true if replacing the memmove with a Move is
|
||||
// safe, either because Move is small or because the arguments are disjoint.
|
||||
// safe, either because Move will do all of its loads before any of its stores, or
|
||||
// because the arguments are known to be disjoint.
|
||||
// This is used as a check for replacing memmove with Move ops.
|
||||
func isInlinableMemmove(dst, src *Value, sz int64, c *Config) bool {
|
||||
// It is always safe to convert memmove into Move when its arguments are disjoint.
|
||||
@@ -1381,6 +1382,9 @@ func isInlinableMemmove(dst, src *Value, sz int64, c *Config) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
func IsInlinableMemmove(dst, src *Value, sz int64, c *Config) bool {
|
||||
return isInlinableMemmove(dst, src, sz, c)
|
||||
}
|
||||
|
||||
// logLargeCopy logs the occurrence of a large copy.
|
||||
// The best place to do this is in the rewrite rules where the size of the move is easy to find.
|
||||
@@ -1394,6 +1398,14 @@ func logLargeCopy(v *Value, s int64) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
func LogLargeCopy(funcName string, pos src.XPos, s int64) {
|
||||
if s < 128 {
|
||||
return
|
||||
}
|
||||
if logopt.Enabled() {
|
||||
logopt.LogOpt(pos, "copy", "lower", funcName, fmt.Sprintf("%d bytes", s))
|
||||
}
|
||||
}
|
||||
|
||||
// hasSmallRotate reports whether the architecture has rotate instructions
|
||||
// for sizes < 32-bit. This is used to decide whether to promote some rotations.
|
||||
|
||||
@@ -96,6 +96,7 @@ func needAlloc(n *ir.Name) bool {
|
||||
func (s *ssafn) AllocFrame(f *ssa.Func) {
|
||||
s.stksize = 0
|
||||
s.stkptrsize = 0
|
||||
s.stkalign = int64(types.RegSize)
|
||||
fn := s.curfn
|
||||
|
||||
// Mark the PAUTO's unused.
|
||||
@@ -160,6 +161,9 @@ func (s *ssafn) AllocFrame(f *ssa.Func) {
|
||||
}
|
||||
s.stksize += w
|
||||
s.stksize = types.Rnd(s.stksize, n.Type().Alignment())
|
||||
if n.Type().Alignment() > int64(types.RegSize) {
|
||||
s.stkalign = n.Type().Alignment()
|
||||
}
|
||||
if n.Type().HasPointers() {
|
||||
s.stkptrsize = s.stksize
|
||||
lastHasPtr = true
|
||||
@@ -169,8 +173,8 @@ func (s *ssafn) AllocFrame(f *ssa.Func) {
|
||||
n.SetFrameOffset(-s.stksize)
|
||||
}
|
||||
|
||||
s.stksize = types.Rnd(s.stksize, int64(types.RegSize))
|
||||
s.stkptrsize = types.Rnd(s.stkptrsize, int64(types.RegSize))
|
||||
s.stksize = types.Rnd(s.stksize, s.stkalign)
|
||||
s.stkptrsize = types.Rnd(s.stkptrsize, s.stkalign)
|
||||
}
|
||||
|
||||
const maxStackSize = 1 << 30
|
||||
|
||||
@@ -105,6 +105,7 @@ func InitConfig() {
|
||||
ir.Syms.GCWriteBarrier = typecheck.LookupRuntimeFunc("gcWriteBarrier")
|
||||
ir.Syms.Goschedguarded = typecheck.LookupRuntimeFunc("goschedguarded")
|
||||
ir.Syms.Growslice = typecheck.LookupRuntimeFunc("growslice")
|
||||
ir.Syms.Memmove = typecheck.LookupRuntimeFunc("memmove")
|
||||
ir.Syms.Msanread = typecheck.LookupRuntimeFunc("msanread")
|
||||
ir.Syms.Msanwrite = typecheck.LookupRuntimeFunc("msanwrite")
|
||||
ir.Syms.Msanmove = typecheck.LookupRuntimeFunc("msanmove")
|
||||
@@ -1359,7 +1360,47 @@ func (s *state) zero(t *types.Type, dst *ssa.Value) {
|
||||
}
|
||||
|
||||
func (s *state) move(t *types.Type, dst, src *ssa.Value) {
|
||||
s.moveWhichMayOverlap(t, dst, src, false)
|
||||
}
|
||||
func (s *state) moveWhichMayOverlap(t *types.Type, dst, src *ssa.Value, mayOverlap bool) {
|
||||
s.instrumentMove(t, dst, src)
|
||||
if mayOverlap && t.IsArray() && t.NumElem() > 1 && !ssa.IsInlinableMemmove(dst, src, t.Size(), s.f.Config) {
|
||||
// Normally, when moving Go values of type T from one location to another,
|
||||
// we don't need to worry about partial overlaps. The two Ts must either be
|
||||
// in disjoint (nonoverlapping) memory or in exactly the same location.
|
||||
// There are 2 cases where this isn't true:
|
||||
// 1) Using unsafe you can arrange partial overlaps.
|
||||
// 2) Since Go 1.17, you can use a cast from a slice to a ptr-to-array.
|
||||
// https://go.dev/ref/spec#Conversions_from_slice_to_array_pointer
|
||||
// This feature can be used to construct partial overlaps of array types.
|
||||
// var a [3]int
|
||||
// p := (*[2]int)(a[:])
|
||||
// q := (*[2]int)(a[1:])
|
||||
// *p = *q
|
||||
// We don't care about solving 1. Or at least, we haven't historically
|
||||
// and no one has complained.
|
||||
// For 2, we need to ensure that if there might be partial overlap,
|
||||
// then we can't use OpMove; we must use memmove instead.
|
||||
// (memmove handles partial overlap by copying in the correct
|
||||
// direction. OpMove does not.)
|
||||
//
|
||||
// Note that we have to be careful here not to introduce a call when
|
||||
// we're marshaling arguments to a call or unmarshaling results from a call.
|
||||
// Cases where this is happening must pass mayOverlap to false.
|
||||
// (Currently this only happens when unmarshaling results of a call.)
|
||||
if t.HasPointers() {
|
||||
s.rtcall(ir.Syms.Typedmemmove, true, nil, s.reflectType(t), dst, src)
|
||||
// We would have otherwise implemented this move with straightline code,
|
||||
// including a write barrier. Pretend we issue a write barrier here,
|
||||
// so that the write barrier tests work. (Otherwise they'd need to know
|
||||
// the details of IsInlineableMemmove.)
|
||||
s.curfn.SetWBPos(s.peekPos())
|
||||
} else {
|
||||
s.rtcall(ir.Syms.Memmove, true, nil, dst, src, s.constInt(types.Types[types.TUINTPTR], t.Size()))
|
||||
}
|
||||
ssa.LogLargeCopy(s.f.Name, s.peekPos(), t.Size())
|
||||
return
|
||||
}
|
||||
store := s.newValue3I(ssa.OpMove, types.TypeMem, t.Size(), dst, src, s.mem())
|
||||
store.Aux = t
|
||||
s.vars[memVar] = store
|
||||
@@ -1541,6 +1582,36 @@ func (s *state) stmt(n ir.Node) {
|
||||
return
|
||||
}
|
||||
|
||||
// mayOverlap keeps track of whether the LHS and RHS might
|
||||
// refer to overlapping memory.
|
||||
mayOverlap := true
|
||||
if n.Y == nil {
|
||||
// Not a move at all, mayOverlap is not relevant.
|
||||
} else if n.Def {
|
||||
// A variable being defined cannot overlap anything else.
|
||||
mayOverlap = false
|
||||
} else if n.X.Op() == ir.ONAME && n.Y.Op() == ir.ONAME {
|
||||
// Two named things never overlap.
|
||||
// (Or they are identical, which we treat as nonoverlapping.)
|
||||
mayOverlap = false
|
||||
} else if n.Y.Op() == ir.ODEREF {
|
||||
p := n.Y.(*ir.StarExpr).X
|
||||
for p.Op() == ir.OCONVNOP {
|
||||
p = p.(*ir.ConvExpr).X
|
||||
}
|
||||
if p.Op() == ir.OSPTR && p.(*ir.UnaryExpr).X.Type().IsString() {
|
||||
// Pointer fields of strings point to unmodifiable memory.
|
||||
// That memory can't overlap with the memory being written.
|
||||
mayOverlap = false
|
||||
}
|
||||
} else if n.Y.Op() == ir.ORESULT || n.Y.Op() == ir.OCALLFUNC || n.Y.Op() == ir.OCALLINTER {
|
||||
// When copying values out of the return area of a call, we know
|
||||
// the source and destination don't overlap. Importantly, we must
|
||||
// set mayOverlap so we don't introduce a call to memmove while
|
||||
// we still have live data in the argument area.
|
||||
mayOverlap = false
|
||||
}
|
||||
|
||||
// Evaluate RHS.
|
||||
rhs := n.Y
|
||||
if rhs != nil {
|
||||
@@ -1641,7 +1712,7 @@ func (s *state) stmt(n ir.Node) {
|
||||
}
|
||||
}
|
||||
|
||||
s.assign(n.X, r, deref, skip)
|
||||
s.assignWhichMayOverlap(n.X, r, deref, skip, mayOverlap)
|
||||
|
||||
case ir.OIF:
|
||||
n := n.(*ir.IfStmt)
|
||||
@@ -3564,7 +3635,11 @@ const (
|
||||
// If deref is true, then we do left = *right instead (and right has already been nil-checked).
|
||||
// If deref is true and right == nil, just do left = 0.
|
||||
// skip indicates assignments (at the top level) that can be avoided.
|
||||
// mayOverlap indicates whether left&right might partially overlap in memory. Default is false.
|
||||
func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask) {
|
||||
s.assignWhichMayOverlap(left, right, deref, skip, false)
|
||||
}
|
||||
func (s *state) assignWhichMayOverlap(left ir.Node, right *ssa.Value, deref bool, skip skipMask, mayOverlap bool) {
|
||||
if left.Op() == ir.ONAME && ir.IsBlank(left) {
|
||||
return
|
||||
}
|
||||
@@ -3665,7 +3740,7 @@ func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask
|
||||
if right == nil {
|
||||
s.zero(t, addr)
|
||||
} else {
|
||||
s.move(t, addr, right)
|
||||
s.moveWhichMayOverlap(t, addr, right, mayOverlap)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -7262,7 +7337,8 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
||||
func defframe(s *State, e *ssafn, f *ssa.Func) {
|
||||
pp := s.pp
|
||||
|
||||
frame := types.Rnd(s.maxarg+e.stksize, int64(types.RegSize))
|
||||
s.maxarg = types.Rnd(s.maxarg, e.stkalign)
|
||||
frame := s.maxarg + e.stksize
|
||||
if Arch.PadFrame != nil {
|
||||
frame = Arch.PadFrame(frame)
|
||||
}
|
||||
@@ -7700,7 +7776,14 @@ type ssafn struct {
|
||||
strings map[string]*obj.LSym // map from constant string to data symbols
|
||||
stksize int64 // stack size for current frame
|
||||
stkptrsize int64 // prefix of stack containing pointers
|
||||
log bool // print ssa debug to the stdout
|
||||
|
||||
// alignment for current frame.
|
||||
// NOTE: when stkalign > PtrSize, currently this only ensures the offsets of
|
||||
// objects in the stack frame are aligned. The stack pointer is still aligned
|
||||
// only PtrSize.
|
||||
stkalign int64
|
||||
|
||||
log bool // print ssa debug to the stdout
|
||||
}
|
||||
|
||||
// StringData returns a symbol which
|
||||
|
||||
@@ -1928,7 +1928,9 @@ func (w *exportWriter) expr(n ir.Node) {
|
||||
w.op(n.Op())
|
||||
w.pos(n.Pos())
|
||||
w.expr(n.X)
|
||||
w.expr(n.RType)
|
||||
if w.bool(n.RType != nil) {
|
||||
w.expr(n.RType)
|
||||
}
|
||||
if w.bool(n.ITab != nil) {
|
||||
w.expr(n.ITab)
|
||||
}
|
||||
|
||||
@@ -1465,7 +1465,10 @@ func (r *importReader) node() ir.Node {
|
||||
return n
|
||||
|
||||
case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2:
|
||||
n := ir.NewDynamicTypeAssertExpr(r.pos(), op, r.expr(), r.expr())
|
||||
n := ir.NewDynamicTypeAssertExpr(r.pos(), op, r.expr(), nil)
|
||||
if r.bool() {
|
||||
n.RType = r.expr()
|
||||
}
|
||||
if r.bool() {
|
||||
n.ITab = r.expr()
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func (s *StdSizes) Alignof(T Type) int64 {
|
||||
// is the same as unsafe.Alignof(x[0]), but at least 1."
|
||||
return s.Alignof(t.elem)
|
||||
case *Struct:
|
||||
if len(t.fields) == 0 && isSyncAtomicAlign64(T) {
|
||||
if len(t.fields) == 0 && IsSyncAtomicAlign64(T) {
|
||||
// Special case: sync/atomic.align64 is an
|
||||
// empty struct we recognize as a signal that
|
||||
// the struct it contains must be
|
||||
@@ -104,7 +104,7 @@ func (s *StdSizes) Alignof(T Type) int64 {
|
||||
return a
|
||||
}
|
||||
|
||||
func isSyncAtomicAlign64(T Type) bool {
|
||||
func IsSyncAtomicAlign64(T Type) bool {
|
||||
named, ok := T.(*Named)
|
||||
if !ok {
|
||||
return false
|
||||
|
||||
3
src/cmd/dist/build.go
vendored
3
src/cmd/dist/build.go
vendored
@@ -938,7 +938,8 @@ func packagefile(pkg string) string {
|
||||
}
|
||||
|
||||
// unixOS is the set of GOOS values matched by the "unix" build tag.
|
||||
// This is the same list as in go/build/syslist.go.
|
||||
// This is the same list as in go/build/syslist.go and
|
||||
// cmd/go/internal/imports/build.go.
|
||||
var unixOS = map[string]bool{
|
||||
"aix": true,
|
||||
"android": true,
|
||||
|
||||
@@ -3,7 +3,7 @@ module cmd
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/google/pprof v0.0.0-20220517023622-154dc81eb7b0
|
||||
github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1
|
||||
golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
|
||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
github.com/google/pprof v0.0.0-20220517023622-154dc81eb7b0 h1:XgEFTOJTsN3Li0Txfhn2UzsysGJfXIDe7wE07uY7ZfI=
|
||||
github.com/google/pprof v0.0.0-20220517023622-154dc81eb7b0/go.mod h1:gSuNB+gJaOiQKLEZ+q+PK9Mq3SOzhRcw2GsGS/FhYDk=
|
||||
github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1 h1:8pyqKJvrJqUYaKS851Ule26pwWvey6IDMiczaBLDKLQ=
|
||||
github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1/go.mod h1:gSuNB+gJaOiQKLEZ+q+PK9Mq3SOzhRcw2GsGS/FhYDk=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2 h1:rcanfLhLDA8nozr/K289V1zcntHr3V+SHlXwzz1ZI2g=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 h1:GVfVkciLYxn5mY5EncwAe0SXUn9Rm81rRkZ0TTmn/cU=
|
||||
|
||||
@@ -174,8 +174,8 @@ func ExtraEnvVars() []cfg.EnvVar {
|
||||
// ExtraEnvVarsCostly returns environment variables that should not leak into child processes
|
||||
// but are costly to evaluate.
|
||||
func ExtraEnvVarsCostly() []cfg.EnvVar {
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
|
||||
cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{})
|
||||
if err != nil {
|
||||
// Should not happen - b.CFlags was given an empty package.
|
||||
|
||||
@@ -20,6 +20,7 @@ package imports
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/go/internal/cfg"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build/constraint"
|
||||
@@ -201,17 +202,22 @@ func matchTag(name string, tags map[string]bool, prefer bool) bool {
|
||||
return prefer
|
||||
}
|
||||
|
||||
have := tags[name]
|
||||
if name == "linux" {
|
||||
have = have || tags["android"]
|
||||
if tags[name] {
|
||||
return true
|
||||
}
|
||||
if name == "solaris" {
|
||||
have = have || tags["illumos"]
|
||||
|
||||
switch name {
|
||||
case "linux":
|
||||
return tags["android"]
|
||||
case "solaris":
|
||||
return tags["illumos"]
|
||||
case "darwin":
|
||||
return tags["ios"]
|
||||
case "unix":
|
||||
return unixOS[cfg.BuildContext.GOOS]
|
||||
default:
|
||||
return false
|
||||
}
|
||||
if name == "darwin" {
|
||||
have = have || tags["ios"]
|
||||
}
|
||||
return have
|
||||
}
|
||||
|
||||
// eval is like
|
||||
@@ -322,6 +328,24 @@ var KnownOS = map[string]bool{
|
||||
"zos": true,
|
||||
}
|
||||
|
||||
// unixOS is the set of GOOS values matched by the "unix" build tag.
|
||||
// This is not used for filename matching.
|
||||
// This is the same list as in go/build/syslist.go and cmd/dist/build.go.
|
||||
var unixOS = map[string]bool{
|
||||
"aix": true,
|
||||
"android": true,
|
||||
"darwin": true,
|
||||
"dragonfly": true,
|
||||
"freebsd": true,
|
||||
"hurd": true,
|
||||
"illumos": true,
|
||||
"ios": true,
|
||||
"linux": true,
|
||||
"netbsd": true,
|
||||
"openbsd": true,
|
||||
"solaris": true,
|
||||
}
|
||||
|
||||
var KnownArch = map[string]bool{
|
||||
"386": true,
|
||||
"amd64": true,
|
||||
|
||||
@@ -36,6 +36,9 @@ func loadTags() map[string]bool {
|
||||
for _, tag := range cfg.BuildContext.BuildTags {
|
||||
tags[tag] = true
|
||||
}
|
||||
for _, tag := range cfg.BuildContext.ToolTags {
|
||||
tags[tag] = true
|
||||
}
|
||||
for _, tag := range cfg.BuildContext.ReleaseTags {
|
||||
tags[tag] = true
|
||||
}
|
||||
|
||||
@@ -689,8 +689,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
|
||||
// Do we need to run a build to gather information?
|
||||
needStale := (listJson && listJsonFields.needAny("Stale", "StaleReason")) || strings.Contains(*listFmt, ".Stale")
|
||||
if needStale || *listExport || *listCompiled {
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
b.IsCmdList = true
|
||||
b.NeedExport = *listExport
|
||||
b.NeedCompiledGoFiles = *listCompiled
|
||||
|
||||
@@ -37,7 +37,9 @@ const (
|
||||
// A Repo represents a code hosting source.
|
||||
// Typical implementations include local version control repositories,
|
||||
// remote version control servers, and code hosting sites.
|
||||
// A Repo must be safe for simultaneous use by multiple goroutines.
|
||||
//
|
||||
// A Repo must be safe for simultaneous use by multiple goroutines,
|
||||
// and callers must not modify returned values, which may be cached and shared.
|
||||
type Repo interface {
|
||||
// CheckReuse checks whether the old origin information
|
||||
// remains up to date. If so, whatever cached object it was
|
||||
|
||||
@@ -348,12 +348,21 @@ func (r *gitRepo) Latest() (*RevInfo, error) {
|
||||
if refs["HEAD"] == "" {
|
||||
return nil, ErrNoCommits
|
||||
}
|
||||
info, err := r.Stat(refs["HEAD"])
|
||||
statInfo, err := r.Stat(refs["HEAD"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Stat may return cached info, so make a copy to modify here.
|
||||
info := new(RevInfo)
|
||||
*info = *statInfo
|
||||
info.Origin = new(Origin)
|
||||
if statInfo.Origin != nil {
|
||||
*info.Origin = *statInfo.Origin
|
||||
}
|
||||
info.Origin.Ref = "HEAD"
|
||||
info.Origin.Hash = refs["HEAD"]
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
@@ -560,7 +569,7 @@ func (r *gitRepo) fetchRefsLocked() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// statLocal returns a RevInfo describing rev in the local git repository.
|
||||
// statLocal returns a new RevInfo describing rev in the local git repository.
|
||||
// It uses version as info.Version.
|
||||
func (r *gitRepo) statLocal(version, rev string) (*RevInfo, error) {
|
||||
out, err := Run(r.dir, "git", "-c", "log.showsignature=false", "log", "--no-decorate", "-n1", "--format=format:%H %ct %D", rev, "--")
|
||||
|
||||
@@ -182,23 +182,27 @@ func mergeOrigin(m1, m2 *codehost.Origin) *codehost.Origin {
|
||||
if !m2.Checkable() {
|
||||
return m2
|
||||
}
|
||||
|
||||
merged := new(codehost.Origin)
|
||||
*merged = *m1 // Clone to avoid overwriting fields in cached results.
|
||||
|
||||
if m2.TagSum != "" {
|
||||
if m1.TagSum != "" && (m1.TagSum != m2.TagSum || m1.TagPrefix != m2.TagPrefix) {
|
||||
m1.ClearCheckable()
|
||||
return m1
|
||||
merged.ClearCheckable()
|
||||
return merged
|
||||
}
|
||||
m1.TagSum = m2.TagSum
|
||||
m1.TagPrefix = m2.TagPrefix
|
||||
merged.TagSum = m2.TagSum
|
||||
merged.TagPrefix = m2.TagPrefix
|
||||
}
|
||||
if m2.Hash != "" {
|
||||
if m1.Hash != "" && (m1.Hash != m2.Hash || m1.Ref != m2.Ref) {
|
||||
m1.ClearCheckable()
|
||||
return m1
|
||||
merged.ClearCheckable()
|
||||
return merged
|
||||
}
|
||||
m1.Hash = m2.Hash
|
||||
m1.Ref = m2.Ref
|
||||
merged.Hash = m2.Hash
|
||||
merged.Ref = m2.Ref
|
||||
}
|
||||
return m1
|
||||
return merged
|
||||
}
|
||||
|
||||
// addVersions fills in m.Versions with the list of known versions.
|
||||
|
||||
@@ -220,6 +220,17 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed
|
||||
return revErr, err
|
||||
}
|
||||
|
||||
mergeRevOrigin := func(rev *modfetch.RevInfo, origin *codehost.Origin) *modfetch.RevInfo {
|
||||
merged := mergeOrigin(rev.Origin, origin)
|
||||
if merged == rev.Origin {
|
||||
return rev
|
||||
}
|
||||
clone := new(modfetch.RevInfo)
|
||||
*clone = *rev
|
||||
clone.Origin = merged
|
||||
return clone
|
||||
}
|
||||
|
||||
lookup := func(v string) (*modfetch.RevInfo, error) {
|
||||
rev, err := repo.Stat(v)
|
||||
// Stat can return a non-nil rev and a non-nil err,
|
||||
@@ -227,7 +238,7 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed
|
||||
if rev == nil && err != nil {
|
||||
return revErr, err
|
||||
}
|
||||
rev.Origin = mergeOrigin(rev.Origin, versions.Origin)
|
||||
rev = mergeRevOrigin(rev, versions.Origin)
|
||||
if err != nil {
|
||||
return rev, err
|
||||
}
|
||||
@@ -256,12 +267,12 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed
|
||||
if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
|
||||
return revErr, err
|
||||
}
|
||||
info, err := repo.Stat(current)
|
||||
if info == nil && err != nil {
|
||||
rev, err = repo.Stat(current)
|
||||
if rev == nil && err != nil {
|
||||
return revErr, err
|
||||
}
|
||||
info.Origin = mergeOrigin(info.Origin, versions.Origin)
|
||||
return info, err
|
||||
rev = mergeRevOrigin(rev, versions.Origin)
|
||||
return rev, err
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,8 +91,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) {
|
||||
}
|
||||
|
||||
work.BuildInit()
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
b.Print = printStderr
|
||||
|
||||
i := 0
|
||||
|
||||
@@ -744,8 +744,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
|
||||
if cfg.BuildI {
|
||||
fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n")
|
||||
@@ -800,7 +799,16 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
|
||||
if !testC || a.Failed {
|
||||
return
|
||||
}
|
||||
b.Init()
|
||||
|
||||
// TODO(bcmills): I have no idea why the Builder must be reset here, but
|
||||
// without this reset dance, TestGoTestDashIDashOWritesBinary fails with
|
||||
// lots of "vet config not found" errors. This was added in CL 5699088,
|
||||
// which had almost no public discussion, a very short commit description,
|
||||
// and left no comment in the code to explain what is going on here. 🤯
|
||||
//
|
||||
// Maybe this has the effect of removing actions that were registered by the
|
||||
// call to CompileAction above?
|
||||
b = work.NewBuilder("")
|
||||
}
|
||||
|
||||
var builds, runs, prints []*work.Action
|
||||
@@ -916,7 +924,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
|
||||
ensureImport(p, "sync/atomic")
|
||||
}
|
||||
|
||||
buildTest, runTest, printTest, err := builderTest(&b, ctx, pkgOpts, p, allImports[p])
|
||||
buildTest, runTest, printTest, err := builderTest(b, ctx, pkgOpts, p, allImports[p])
|
||||
if err != nil {
|
||||
str := err.Error()
|
||||
str = strings.TrimPrefix(str, "\n")
|
||||
|
||||
@@ -94,8 +94,7 @@ func runVet(ctx context.Context, cmd *base.Command, args []string) {
|
||||
base.Fatalf("no packages to vet")
|
||||
}
|
||||
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder("")
|
||||
|
||||
root := &work.Action{Mode: "go vet"}
|
||||
for _, p := range pkgs {
|
||||
|
||||
@@ -240,7 +240,13 @@ const (
|
||||
ModeVetOnly = 1 << 8
|
||||
)
|
||||
|
||||
func (b *Builder) Init() {
|
||||
// NewBuilder returns a new Builder ready for use.
|
||||
//
|
||||
// If workDir is the empty string, NewBuilder creates a WorkDir if needed
|
||||
// and arranges for it to be removed in case of an unclean exit.
|
||||
func NewBuilder(workDir string) *Builder {
|
||||
b := new(Builder)
|
||||
|
||||
b.Print = func(a ...any) (int, error) {
|
||||
return fmt.Fprint(os.Stderr, a...)
|
||||
}
|
||||
@@ -249,7 +255,9 @@ func (b *Builder) Init() {
|
||||
b.toolIDCache = make(map[string]string)
|
||||
b.buildIDCache = make(map[string]string)
|
||||
|
||||
if cfg.BuildN {
|
||||
if workDir != "" {
|
||||
b.WorkDir = workDir
|
||||
} else if cfg.BuildN {
|
||||
b.WorkDir = "$WORK"
|
||||
} else {
|
||||
tmp, err := os.MkdirTemp(cfg.Getenv("GOTMPDIR"), "go-build")
|
||||
@@ -306,6 +314,8 @@ func (b *Builder) Init() {
|
||||
base.Exit()
|
||||
}
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func CheckGOOSARCHPair(goos, goarch string) error {
|
||||
|
||||
@@ -403,8 +403,7 @@ var RuntimeVersion = runtime.Version()
|
||||
func runBuild(ctx context.Context, cmd *base.Command, args []string) {
|
||||
modload.InitWorkfile()
|
||||
BuildInit()
|
||||
var b Builder
|
||||
b.Init()
|
||||
b := NewBuilder("")
|
||||
|
||||
pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{LoadVCS: true}, args)
|
||||
load.CheckPackageErrors(pkgs)
|
||||
@@ -728,8 +727,8 @@ func InstallPackages(ctx context.Context, patterns []string, pkgs []*load.Packag
|
||||
}
|
||||
base.ExitIfErrors()
|
||||
|
||||
var b Builder
|
||||
b.Init()
|
||||
b := NewBuilder("")
|
||||
|
||||
depMode := ModeBuild
|
||||
if cfg.BuildI {
|
||||
depMode = ModeInstall
|
||||
|
||||
@@ -556,10 +556,8 @@ func (ts *testScript) cmdCc(want simpleStatus, args []string) {
|
||||
ts.fatalf("usage: cc args... [&]")
|
||||
}
|
||||
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b := work.NewBuilder(ts.workdir)
|
||||
ts.cmdExec(want, append(b.GccCmd(".", ""), args...))
|
||||
robustio.RemoveAll(b.WorkDir)
|
||||
}
|
||||
|
||||
// cd changes to a different directory.
|
||||
|
||||
32
src/cmd/go/testdata/script/import_unix_tag.txt
vendored
Normal file
32
src/cmd/go/testdata/script/import_unix_tag.txt
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Regression test for https://go.dev/issue/54712: the "unix" build constraint
|
||||
# was not applied consistently during package loading.
|
||||
|
||||
go list -x -f '{{if .Module}}{{.ImportPath}}{{end}}' -deps .
|
||||
stdout 'example.com/version'
|
||||
|
||||
-- go.mod --
|
||||
module example
|
||||
|
||||
go 1.19
|
||||
|
||||
require example.com/version v1.1.0
|
||||
-- go.sum --
|
||||
example.com/version v1.1.0 h1:VdPnGmIF1NJrntStkxGrF3L/OfhaL567VzCjncGUgtM=
|
||||
example.com/version v1.1.0/go.mod h1:S7K9BnT4o5wT4PCczXPfWVzpjD4ud4e7AJMQJEgiu2Q=
|
||||
-- main_notunix.go --
|
||||
//go:build !(aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris)
|
||||
|
||||
package main
|
||||
|
||||
import _ "example.com/version"
|
||||
|
||||
func main() {}
|
||||
|
||||
-- main_unix.go --
|
||||
//go:build unix
|
||||
|
||||
package main
|
||||
|
||||
import _ "example.com/version"
|
||||
|
||||
func main() {}
|
||||
29
src/cmd/go/testdata/script/test_race_tag.txt
vendored
Normal file
29
src/cmd/go/testdata/script/test_race_tag.txt
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# Tests Issue #54468
|
||||
|
||||
[short] skip 'links a test binary'
|
||||
[!race] skip
|
||||
|
||||
go mod tidy
|
||||
go test -c -o=$devnull -race .
|
||||
|
||||
! stderr 'cannot find package'
|
||||
|
||||
-- go.mod --
|
||||
module testrace
|
||||
|
||||
go 1.18
|
||||
|
||||
require rsc.io/sampler v1.0.0
|
||||
-- race_test.go --
|
||||
//go:build race
|
||||
|
||||
package testrace
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
_ "rsc.io/sampler"
|
||||
)
|
||||
|
||||
func TestRaceTag(t *testing.T) {
|
||||
}
|
||||
@@ -33,6 +33,8 @@ import (
|
||||
// See issues 36852, 41409, and 43687.
|
||||
// (Also see golang.org/issue/27348.)
|
||||
func TestAllDependencies(t *testing.T) {
|
||||
t.Skip("TODO(#54376): 1.19.1 contains unreleased changes from vendored modules")
|
||||
|
||||
goBin := testenv.GoToolPath(t)
|
||||
|
||||
// Ensure that all packages imported within GOROOT
|
||||
|
||||
@@ -1842,3 +1842,82 @@ func main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestIssue54320(t *testing.T) {
|
||||
// Check that when trampolines are used, the DWARF LPT is correctly
|
||||
// emitted in the final binary
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
if runtime.GOOS == "plan9" {
|
||||
t.Skip("skipping on plan9; no DWARF symbol table in executables")
|
||||
}
|
||||
|
||||
t.Parallel()
|
||||
|
||||
const prog = `
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Hello world\n");
|
||||
}
|
||||
`
|
||||
|
||||
dir := t.TempDir()
|
||||
f := gobuild(t, dir, prog, "-ldflags=-debugtramp=2")
|
||||
defer f.Close()
|
||||
|
||||
d, err := f.DWARF()
|
||||
if err != nil {
|
||||
t.Fatalf("error reading DWARF: %v", err)
|
||||
}
|
||||
|
||||
rdr := d.Reader()
|
||||
found := false
|
||||
var entry *dwarf.Entry
|
||||
for entry, err = rdr.Next(); entry != nil; entry, err = rdr.Next() {
|
||||
if err != nil {
|
||||
t.Fatalf("error reading DWARF: %v", err)
|
||||
}
|
||||
if entry.Tag != dwarf.TagCompileUnit {
|
||||
continue
|
||||
}
|
||||
name, _ := entry.Val(dwarf.AttrName).(string)
|
||||
if name == "main" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
rdr.SkipChildren()
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("could not find main compile unit")
|
||||
}
|
||||
lr, err := d.LineReader(entry)
|
||||
if err != nil {
|
||||
t.Fatalf("error obtaining linereader: %v", err)
|
||||
}
|
||||
|
||||
var le dwarf.LineEntry
|
||||
found = false
|
||||
for {
|
||||
if err := lr.Next(&le); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
t.Fatalf("error reading linentry: %v", err)
|
||||
}
|
||||
// check LE contains an entry to test.go
|
||||
if le.File == nil {
|
||||
continue
|
||||
}
|
||||
file := filepath.Base(le.File.Name)
|
||||
if file == "test.go" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("no LPT entries for test.go")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1610,13 +1610,8 @@ func (l *Loader) GetFuncDwarfAuxSyms(fnSymIdx Sym) (auxDwarfInfo, auxDwarfLoc, a
|
||||
if l.SymType(fnSymIdx) != sym.STEXT {
|
||||
log.Fatalf("error: non-function sym %d/%s t=%s passed to GetFuncDwarfAuxSyms", fnSymIdx, l.SymName(fnSymIdx), l.SymType(fnSymIdx).String())
|
||||
}
|
||||
if l.IsExternal(fnSymIdx) {
|
||||
// Current expectation is that any external function will
|
||||
// not have auxsyms.
|
||||
return
|
||||
}
|
||||
r, li := l.toLocal(fnSymIdx)
|
||||
auxs := r.Auxs(li)
|
||||
r, auxs := l.auxs(fnSymIdx)
|
||||
|
||||
for i := range auxs {
|
||||
a := &auxs[i]
|
||||
switch a.Type() {
|
||||
|
||||
8
src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go
generated
vendored
8
src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go
generated
vendored
@@ -79,7 +79,7 @@ type configMenuEntry struct {
|
||||
}
|
||||
|
||||
// configMenu returns a list of items to add to a menu in the web UI.
|
||||
func configMenu(fname string, url url.URL) []configMenuEntry {
|
||||
func configMenu(fname string, u url.URL) []configMenuEntry {
|
||||
// Start with system configs.
|
||||
configs := []namedConfig{{Name: "Default", config: defaultConfig()}}
|
||||
if settings, err := readSettings(fname); err == nil {
|
||||
@@ -91,13 +91,15 @@ func configMenu(fname string, url url.URL) []configMenuEntry {
|
||||
result := make([]configMenuEntry, len(configs))
|
||||
lastMatch := -1
|
||||
for i, cfg := range configs {
|
||||
dst, changed := cfg.config.makeURL(url)
|
||||
dst, changed := cfg.config.makeURL(u)
|
||||
if !changed {
|
||||
lastMatch = i
|
||||
}
|
||||
// Use a relative URL to work in presence of stripping/redirects in webui.go.
|
||||
rel := &url.URL{RawQuery: dst.RawQuery, ForceQuery: true}
|
||||
result[i] = configMenuEntry{
|
||||
Name: cfg.Name,
|
||||
URL: dst.String(),
|
||||
URL: rel.String(),
|
||||
UserConfig: (i != 0),
|
||||
}
|
||||
}
|
||||
|
||||
3
src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go
generated
vendored
3
src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go
generated
vendored
@@ -385,6 +385,9 @@ func multilinePrintableName(info *NodeInfo) string {
|
||||
infoCopy := *info
|
||||
infoCopy.Name = escapeForDot(ShortenFunctionName(infoCopy.Name))
|
||||
infoCopy.Name = strings.Replace(infoCopy.Name, "::", `\n`, -1)
|
||||
// Go type parameters are reported as "[...]" by Go pprof profiles.
|
||||
// Keep this ellipsis rather than replacing with newlines below.
|
||||
infoCopy.Name = strings.Replace(infoCopy.Name, "[...]", "[…]", -1)
|
||||
infoCopy.Name = strings.Replace(infoCopy.Name, ".", `\n`, -1)
|
||||
if infoCopy.File != "" {
|
||||
infoCopy.File = filepath.Base(infoCopy.File)
|
||||
|
||||
61
src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go
generated
vendored
61
src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go
generated
vendored
@@ -205,49 +205,64 @@ func Demangle(prof *profile.Profile, force bool, demanglerMode string) {
|
||||
}
|
||||
}
|
||||
|
||||
var options []demangle.Option
|
||||
options := demanglerModeToOptions(demanglerMode)
|
||||
for _, fn := range prof.Function {
|
||||
demangleSingleFunction(fn, options)
|
||||
}
|
||||
}
|
||||
|
||||
func demanglerModeToOptions(demanglerMode string) []demangle.Option {
|
||||
switch demanglerMode {
|
||||
case "": // demangled, simplified: no parameters, no templates, no return type
|
||||
options = []demangle.Option{demangle.NoParams, demangle.NoTemplateParams}
|
||||
return []demangle.Option{demangle.NoParams, demangle.NoTemplateParams}
|
||||
case "templates": // demangled, simplified: no parameters, no return type
|
||||
options = []demangle.Option{demangle.NoParams}
|
||||
return []demangle.Option{demangle.NoParams}
|
||||
case "full":
|
||||
options = []demangle.Option{demangle.NoClones}
|
||||
return []demangle.Option{demangle.NoClones}
|
||||
case "none": // no demangling
|
||||
return
|
||||
return []demangle.Option{}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unknown demanglerMode %s", demanglerMode))
|
||||
}
|
||||
|
||||
func demangleSingleFunction(fn *profile.Function, options []demangle.Option) {
|
||||
if fn.Name != "" && fn.SystemName != fn.Name {
|
||||
return // Already demangled.
|
||||
}
|
||||
// Copy the options because they may be updated by the call.
|
||||
o := make([]demangle.Option, len(options))
|
||||
for _, fn := range prof.Function {
|
||||
if fn.Name != "" && fn.SystemName != fn.Name {
|
||||
continue // Already demangled.
|
||||
}
|
||||
copy(o, options)
|
||||
if demangled := demangle.Filter(fn.SystemName, o...); demangled != fn.SystemName {
|
||||
fn.Name = demangled
|
||||
continue
|
||||
}
|
||||
// Could not demangle. Apply heuristics in case the name is
|
||||
// already demangled.
|
||||
name := fn.SystemName
|
||||
if looksLikeDemangledCPlusPlus(name) {
|
||||
if demanglerMode == "" || demanglerMode == "templates" {
|
||||
copy(o, options)
|
||||
if demangled := demangle.Filter(fn.SystemName, o...); demangled != fn.SystemName {
|
||||
fn.Name = demangled
|
||||
return
|
||||
}
|
||||
// Could not demangle. Apply heuristics in case the name is
|
||||
// already demangled.
|
||||
name := fn.SystemName
|
||||
if looksLikeDemangledCPlusPlus(name) {
|
||||
for _, o := range options {
|
||||
switch o {
|
||||
case demangle.NoParams:
|
||||
name = removeMatching(name, '(', ')')
|
||||
}
|
||||
if demanglerMode == "" {
|
||||
case demangle.NoTemplateParams:
|
||||
name = removeMatching(name, '<', '>')
|
||||
}
|
||||
}
|
||||
fn.Name = name
|
||||
}
|
||||
fn.Name = name
|
||||
}
|
||||
|
||||
// looksLikeDemangledCPlusPlus is a heuristic to decide if a name is
|
||||
// the result of demangling C++. If so, further heuristics will be
|
||||
// applied to simplify the name.
|
||||
func looksLikeDemangledCPlusPlus(demangled string) bool {
|
||||
if strings.Contains(demangled, ".<") { // Skip java names of the form "class.<init>"
|
||||
// Skip java names of the form "class.<init>".
|
||||
if strings.Contains(demangled, ".<") {
|
||||
return false
|
||||
}
|
||||
// Skip Go names of the form "foo.(*Bar[...]).Method".
|
||||
if strings.Contains(demangled, "]).") {
|
||||
return false
|
||||
}
|
||||
return strings.ContainsAny(demangled, "<>[]") || strings.Contains(demangled, "::")
|
||||
|
||||
2
src/cmd/vendor/modules.txt
vendored
2
src/cmd/vendor/modules.txt
vendored
@@ -1,4 +1,4 @@
|
||||
# github.com/google/pprof v0.0.0-20220517023622-154dc81eb7b0
|
||||
# github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1
|
||||
## explicit; go 1.17
|
||||
github.com/google/pprof/driver
|
||||
github.com/google/pprof/internal/binutils
|
||||
|
||||
@@ -240,7 +240,7 @@ func (hs *serverHandshakeState) processClientHello() error {
|
||||
|
||||
hs.ecdheOk = supportsECDHE(c.config, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints)
|
||||
|
||||
if hs.ecdheOk {
|
||||
if hs.ecdheOk && len(hs.clientHello.supportedPoints) > 0 {
|
||||
// Although omitting the ec_point_formats extension is permitted, some
|
||||
// old OpenSSL version will refuse to handshake if not present.
|
||||
//
|
||||
@@ -321,6 +321,13 @@ func supportsECDHE(c *Config, supportedCurves []CurveID, supportedPoints []uint8
|
||||
break
|
||||
}
|
||||
}
|
||||
// Per RFC 8422, Section 5.1.2, if the Supported Point Formats extension is
|
||||
// missing, uncompressed points are supported. If supportedPoints is empty,
|
||||
// the extension must be missing, as an empty extension body is rejected by
|
||||
// the parser. See https://go.dev/issue/49126.
|
||||
if len(supportedPoints) == 0 {
|
||||
supportsPointFormat = true
|
||||
}
|
||||
|
||||
return supportsCurve && supportsPointFormat
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ func TestTLS12OnlyCipherSuites(t *testing.T) {
|
||||
|
||||
func TestTLSPointFormats(t *testing.T) {
|
||||
// Test that a Server returns the ec_point_format extension when ECC is
|
||||
// negotiated, and not returned on RSA handshake.
|
||||
// negotiated, and not on a RSA handshake or if ec_point_format is missing.
|
||||
tests := []struct {
|
||||
name string
|
||||
cipherSuites []uint16
|
||||
@@ -289,8 +289,11 @@ func TestTLSPointFormats(t *testing.T) {
|
||||
supportedPoints []uint8
|
||||
wantSupportedPoints bool
|
||||
}{
|
||||
{"ECC", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, []uint8{compressionNone}, true},
|
||||
{"ECC", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, []uint8{pointFormatUncompressed}, true},
|
||||
{"ECC without ec_point_format", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, nil, false},
|
||||
{"ECC with extra values", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, []uint8{13, 37, pointFormatUncompressed, 42}, true},
|
||||
{"RSA", []uint16{TLS_RSA_WITH_AES_256_GCM_SHA384}, nil, nil, false},
|
||||
{"RSA with ec_point_format", []uint16{TLS_RSA_WITH_AES_256_GCM_SHA384}, nil, []uint8{pointFormatUncompressed}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -330,18 +333,8 @@ func TestTLSPointFormats(t *testing.T) {
|
||||
t.Fatalf("didn't get ServerHello message in reply. Got %v\n", reply)
|
||||
}
|
||||
if tt.wantSupportedPoints {
|
||||
if len(serverHello.supportedPoints) < 1 {
|
||||
t.Fatal("missing ec_point_format extension from server")
|
||||
}
|
||||
found := false
|
||||
for _, p := range serverHello.supportedPoints {
|
||||
if p == pointFormatUncompressed {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("missing uncompressed format in ec_point_format extension from server")
|
||||
if !bytes.Equal(serverHello.supportedPoints, []uint8{pointFormatUncompressed}) {
|
||||
t.Fatal("incorrect ec_point_format extension from server")
|
||||
}
|
||||
} else {
|
||||
if len(serverHello.supportedPoints) != 0 {
|
||||
|
||||
@@ -54,6 +54,9 @@ func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) {
|
||||
// marshalECPrivateKey marshals an EC private key into ASN.1, DER format and
|
||||
// sets the curve ID to the given OID, or omits it if OID is nil.
|
||||
func marshalECPrivateKeyWithOID(key *ecdsa.PrivateKey, oid asn1.ObjectIdentifier) ([]byte, error) {
|
||||
if !key.Curve.IsOnCurve(key.X, key.Y) {
|
||||
return nil, errors.New("invalid elliptic key public key")
|
||||
}
|
||||
privateKey := make([]byte, (key.Curve.Params().N.BitLen()+7)/8)
|
||||
return asn1.Marshal(ecPrivateKey{
|
||||
Version: 1,
|
||||
|
||||
@@ -84,11 +84,14 @@ func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.A
|
||||
// RFC 3279, Section 2.3.1.
|
||||
publicKeyAlgorithm.Parameters = asn1.NullRawValue
|
||||
case *ecdsa.PublicKey:
|
||||
publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
|
||||
oid, ok := oidFromNamedCurve(pub.Curve)
|
||||
if !ok {
|
||||
return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported elliptic curve")
|
||||
}
|
||||
if !pub.Curve.IsOnCurve(pub.X, pub.Y) {
|
||||
return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: invalid elliptic curve public key")
|
||||
}
|
||||
publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
|
||||
publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA
|
||||
var paramBytes []byte
|
||||
paramBytes, err = asn1.Marshal(oid)
|
||||
|
||||
@@ -68,6 +68,20 @@ func TestPKCS1MismatchPublicKeyFormat(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalInvalidPublicKey(t *testing.T) {
|
||||
_, err := MarshalPKIXPublicKey(&ecdsa.PublicKey{})
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got MarshalPKIXPublicKey success")
|
||||
}
|
||||
_, err = MarshalPKIXPublicKey(&ecdsa.PublicKey{
|
||||
Curve: elliptic.P256(),
|
||||
X: big.NewInt(1), Y: big.NewInt(2),
|
||||
})
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got MarshalPKIXPublicKey success")
|
||||
}
|
||||
}
|
||||
|
||||
func testParsePKIXPublicKey(t *testing.T, pemBytes string) (pub any) {
|
||||
block, _ := pem.Decode([]byte(pemBytes))
|
||||
pub, err := ParsePKIXPublicKey(block.Bytes)
|
||||
|
||||
@@ -33,7 +33,8 @@ var knownOS = map[string]bool{
|
||||
|
||||
// unixOS is the set of GOOS values matched by the "unix" build tag.
|
||||
// This is not used for filename matching.
|
||||
// This list also appears in cmd/dist/build.go.
|
||||
// This list also appears in cmd/dist/build.go and
|
||||
// cmd/go/internal/imports/build.go.
|
||||
var unixOS = map[string]bool{
|
||||
"aix": true,
|
||||
"android": true,
|
||||
|
||||
@@ -5058,6 +5058,9 @@ func (sc *http2serverConn) startGracefulShutdownInternal() {
|
||||
func (sc *http2serverConn) goAway(code http2ErrCode) {
|
||||
sc.serveG.check()
|
||||
if sc.inGoAway {
|
||||
if sc.goAwayCode == http2ErrCodeNo {
|
||||
sc.goAwayCode = code
|
||||
}
|
||||
return
|
||||
}
|
||||
sc.inGoAway = true
|
||||
|
||||
@@ -1191,17 +1191,23 @@ func (u *URL) UnmarshalBinary(text []byte) error {
|
||||
// any existing path and the resulting path cleaned of any ./ or ../ elements.
|
||||
// Any sequences of multiple / characters will be reduced to a single /.
|
||||
func (u *URL) JoinPath(elem ...string) *URL {
|
||||
url := *u
|
||||
if len(elem) > 0 {
|
||||
elem = append([]string{u.EscapedPath()}, elem...)
|
||||
p := path.Join(elem...)
|
||||
// path.Join will remove any trailing slashes.
|
||||
// Preserve at least one.
|
||||
if strings.HasSuffix(elem[len(elem)-1], "/") && !strings.HasSuffix(p, "/") {
|
||||
p += "/"
|
||||
}
|
||||
url.setPath(p)
|
||||
elem = append([]string{u.EscapedPath()}, elem...)
|
||||
var p string
|
||||
if !strings.HasPrefix(elem[0], "/") {
|
||||
// Return a relative path if u is relative,
|
||||
// but ensure that it contains no ../ elements.
|
||||
elem[0] = "/" + elem[0]
|
||||
p = path.Join(elem...)[1:]
|
||||
} else {
|
||||
p = path.Join(elem...)
|
||||
}
|
||||
// path.Join will remove any trailing slashes.
|
||||
// Preserve at least one.
|
||||
if strings.HasSuffix(elem[len(elem)-1], "/") && !strings.HasSuffix(p, "/") {
|
||||
p += "/"
|
||||
}
|
||||
url := *u
|
||||
url.setPath(p)
|
||||
return &url
|
||||
}
|
||||
|
||||
|
||||
@@ -2080,6 +2080,26 @@ func TestJoinPath(t *testing.T) {
|
||||
elem: []string{"../../../go"},
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/",
|
||||
elem: []string{"../go"},
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com",
|
||||
elem: []string{"../go"},
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com",
|
||||
elem: []string{"../go", "../../go", "../../../go"},
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/../go",
|
||||
elem: nil,
|
||||
out: "https://go.googlesource.com/go",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/",
|
||||
elem: []string{"./go"},
|
||||
@@ -2112,7 +2132,7 @@ func TestJoinPath(t *testing.T) {
|
||||
{
|
||||
base: "https://go.googlesource.com",
|
||||
elem: nil,
|
||||
out: "https://go.googlesource.com",
|
||||
out: "https://go.googlesource.com/",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/",
|
||||
@@ -2129,11 +2149,46 @@ func TestJoinPath(t *testing.T) {
|
||||
elem: []string{"c%2fd"},
|
||||
out: "https://go.googlesource.com/a%2fb/c%2fd",
|
||||
},
|
||||
{
|
||||
base: "https://go.googlesource.com/a/b",
|
||||
elem: []string{"/go"},
|
||||
out: "https://go.googlesource.com/a/b/go",
|
||||
},
|
||||
{
|
||||
base: "/",
|
||||
elem: nil,
|
||||
out: "/",
|
||||
},
|
||||
{
|
||||
base: "a",
|
||||
elem: nil,
|
||||
out: "a",
|
||||
},
|
||||
{
|
||||
base: "a",
|
||||
elem: []string{"b"},
|
||||
out: "a/b",
|
||||
},
|
||||
{
|
||||
base: "a",
|
||||
elem: []string{"../b"},
|
||||
out: "b",
|
||||
},
|
||||
{
|
||||
base: "a",
|
||||
elem: []string{"../../b"},
|
||||
out: "b",
|
||||
},
|
||||
{
|
||||
base: "",
|
||||
elem: []string{"a"},
|
||||
out: "a",
|
||||
},
|
||||
{
|
||||
base: "",
|
||||
elem: []string{"../a"},
|
||||
out: "a",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
wantErr := "nil"
|
||||
|
||||
@@ -387,6 +387,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
RET
|
||||
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register (R3), and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOVW R13, R13
|
||||
|
||||
MOVW $0, R7
|
||||
B runtime·morestack(SB)
|
||||
|
||||
|
||||
@@ -320,6 +320,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
UNDEF
|
||||
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register (R3), and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOVD RSP, RSP
|
||||
|
||||
MOVW $0, R26
|
||||
B runtime·morestack(SB)
|
||||
|
||||
|
||||
@@ -258,6 +258,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
UNDEF
|
||||
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register (R3), and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOVV R29, R29
|
||||
|
||||
MOVV R0, REGCTXT
|
||||
JMP runtime·morestack(SB)
|
||||
|
||||
|
||||
@@ -257,6 +257,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
UNDEF
|
||||
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register (R3), and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOVW R29, R29
|
||||
|
||||
MOVW R0, REGCTXT
|
||||
JMP runtime·morestack(SB)
|
||||
|
||||
|
||||
@@ -334,6 +334,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
UNDEF
|
||||
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register (R5), and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOVD R1, R1
|
||||
|
||||
MOVD R0, R11
|
||||
BR runtime·morestack(SB)
|
||||
|
||||
|
||||
@@ -158,8 +158,8 @@ TEXT runtime·getcallerpc(SB),NOSPLIT|NOFRAME,$0-8
|
||||
*/
|
||||
|
||||
// Called during function prolog when more stack is needed.
|
||||
// Caller has already loaded:
|
||||
// R1: framesize, R2: argsize, R3: LR
|
||||
// Called with return address (i.e. caller's PC) in X5 (aka T0),
|
||||
// and the LR register contains the caller's LR.
|
||||
//
|
||||
// The traceback routines see morestack on a g0 as being
|
||||
// the top of a stack (for example, morestack calling newstack
|
||||
@@ -209,6 +209,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
|
||||
// func morestack_noctxt()
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register, and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOV X2, X2
|
||||
|
||||
MOV ZERO, CTXT
|
||||
JMP runtime·morestack(SB)
|
||||
|
||||
|
||||
@@ -346,6 +346,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
||||
UNDEF
|
||||
|
||||
TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
// Force SPWRITE. This function doesn't actually write SP,
|
||||
// but it is called with a special calling convention where
|
||||
// the caller doesn't save LR on stack but passes it as a
|
||||
// register (R5), and the unwinder currently doesn't understand.
|
||||
// Make it SPWRITE to stop unwinding. (See issue 54332)
|
||||
MOVD R15, R15
|
||||
|
||||
MOVD $0, R12
|
||||
BR runtime·morestack(SB)
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ TEXT _main<>(SB),NOSPLIT,$-8
|
||||
// There is no TLS base pointer.
|
||||
//
|
||||
// TODO(austin): Support ABI v1 dynamic linking entry point
|
||||
XOR R0, R0 // Note, newer kernels may not always set R0 to 0.
|
||||
MOVD $runtime·rt0_go(SB), R12
|
||||
MOVD R12, CTR
|
||||
MOVBZ runtime·iscgo(SB), R5
|
||||
|
||||
@@ -84,6 +84,10 @@ func (c *sigctxt) fixsigcode(sig uint32) {
|
||||
// in real life, people will probably search for it and find this code.
|
||||
// There are no Google hits for b01dfacedebac1e or 0xb01dfacedebac1e
|
||||
// as I type this comment.
|
||||
//
|
||||
// Note: if this code is removed, please consider
|
||||
// enabling TestSignalForwardingGo for darwin-amd64 in
|
||||
// misc/cgo/testcarchive/carchive_test.go.
|
||||
if c.sigcode() == _SI_USER {
|
||||
c.set_sigcode(_SI_USER + 1)
|
||||
c.set_sigaddr(0xb01dfacedebac1e)
|
||||
|
||||
39
test/fixedbugs/issue53702.go
Normal file
39
test/fixedbugs/issue53702.go
Normal file
@@ -0,0 +1,39 @@
|
||||
// run
|
||||
|
||||
// Copyright 2022 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
|
||||
|
||||
type Elem struct{}
|
||||
|
||||
func (*Elem) Wait(callback func()) {}
|
||||
|
||||
type Base struct {
|
||||
elem [8]*Elem
|
||||
}
|
||||
|
||||
var g_val = 1
|
||||
|
||||
func (s *Base) Do() *int {
|
||||
resp := &g_val
|
||||
for _, e := range s.elem {
|
||||
e.Wait(func() {
|
||||
*resp = 0
|
||||
})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
type Sub struct {
|
||||
*Base
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := Sub{new(Base)}
|
||||
resp := a.Do()
|
||||
if resp != nil && *resp != 1 {
|
||||
panic("FAIL")
|
||||
}
|
||||
}
|
||||
25
test/fixedbugs/issue53982.go
Normal file
25
test/fixedbugs/issue53982.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// build
|
||||
|
||||
// Copyright 2022 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
|
||||
|
||||
type S[K, V any] struct {
|
||||
E[V]
|
||||
}
|
||||
|
||||
type E[K any] struct{}
|
||||
|
||||
func (e E[K]) M() E[K] {
|
||||
return e
|
||||
}
|
||||
|
||||
func G[K, V any](V) {
|
||||
_ = (*S[K, V]).M
|
||||
}
|
||||
|
||||
func main() {
|
||||
G[*int](new(int))
|
||||
}
|
||||
26
test/fixedbugs/issue54220.go
Normal file
26
test/fixedbugs/issue54220.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// run
|
||||
|
||||
// Copyright 2022 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 (
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type t struct {
|
||||
i1 atomic.Int32
|
||||
i2 atomic.Int64
|
||||
}
|
||||
|
||||
var v t
|
||||
|
||||
func main() {
|
||||
if o := unsafe.Offsetof(v.i2); o != 8 {
|
||||
panic("unexpected offset, want: 8, got: " + strconv.Itoa(int(o)))
|
||||
}
|
||||
}
|
||||
26
test/fixedbugs/issue54467.go
Normal file
26
test/fixedbugs/issue54467.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// run
|
||||
|
||||
// Copyright 2022 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"
|
||||
|
||||
func main() {
|
||||
var x [64]byte
|
||||
for i := range x {
|
||||
x[i] = byte(i)
|
||||
}
|
||||
y := x
|
||||
|
||||
copy(x[4:36], x[2:34])
|
||||
*(*[32]byte)(y[4:36]) = *(*[32]byte)(y[2:34])
|
||||
|
||||
for i := range x {
|
||||
if x[i] != y[i] {
|
||||
fmt.Printf("x[%v] = %v; y[%v] = %v\n", i, x[i], i, y[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
40
test/fixedbugs/issue54638.go
Normal file
40
test/fixedbugs/issue54638.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// compile
|
||||
|
||||
// Copyright 2022 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.
|
||||
|
||||
// Issue 54638: composite literal assignment with
|
||||
// alignment > PtrSize causes ICE.
|
||||
|
||||
package p
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
type S struct{ l any }
|
||||
|
||||
type T struct {
|
||||
H any
|
||||
a [14]int64
|
||||
f func()
|
||||
x atomic.Int64
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func (T) M(any) {}
|
||||
|
||||
type W [2]int64
|
||||
|
||||
//go:noinline
|
||||
func (W) Done() {}
|
||||
|
||||
func F(l any) [3]*int {
|
||||
var w W
|
||||
var x [3]*int // use some stack
|
||||
t := T{H: S{l: l}}
|
||||
go func() {
|
||||
t.M(l)
|
||||
w.Done()
|
||||
}()
|
||||
return x
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// errorcheck -0 -d=nil
|
||||
|
||||
// +build !wasm
|
||||
// +build !aix
|
||||
//go:build !wasm && !aix
|
||||
// +build !wasm,!aix
|
||||
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
@@ -20,7 +20,7 @@ func f5(p *float32, q *float64, r *float32, s *float64) float64 {
|
||||
return x + y
|
||||
}
|
||||
|
||||
type T [29]byte
|
||||
type T struct{ b [29]byte }
|
||||
|
||||
func f6(p, q *T) {
|
||||
x := *p // ERROR "removed nil check"
|
||||
@@ -28,6 +28,6 @@ func f6(p, q *T) {
|
||||
}
|
||||
|
||||
// make sure to remove nil check for memory move (issue #18003)
|
||||
func f8(t *[8]int) [8]int {
|
||||
func f8(t *struct{ b [8]int }) struct{ b [8]int } {
|
||||
return *t // ERROR "removed nil check"
|
||||
}
|
||||
|
||||
32
test/typeparam/issue54135.go
Normal file
32
test/typeparam/issue54135.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// run
|
||||
|
||||
// Copyright 2022 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
|
||||
|
||||
type Foo struct{}
|
||||
|
||||
func (Foo) Blanker() {}
|
||||
|
||||
type Bar[T any] interface {
|
||||
Blanker()
|
||||
}
|
||||
|
||||
type Baz interface {
|
||||
Some()
|
||||
}
|
||||
|
||||
func check[T comparable](p Bar[T]) {
|
||||
if x, ok := p.(any); !ok || x != p {
|
||||
panic("FAIL")
|
||||
}
|
||||
if _, ok := p.(Baz); ok {
|
||||
panic("FAIL")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
check[int](Foo{})
|
||||
}
|
||||
20
test/typeparam/issue54302.dir/a.go
Normal file
20
test/typeparam/issue54302.dir/a.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2022 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 a
|
||||
|
||||
func A() {
|
||||
B[int](new(G[int]))
|
||||
}
|
||||
|
||||
func B[T any](iface interface{ M(T) }) {
|
||||
x, ok := iface.(*G[T])
|
||||
if !ok || iface != x {
|
||||
panic("FAIL")
|
||||
}
|
||||
}
|
||||
|
||||
type G[T any] struct{}
|
||||
|
||||
func (*G[T]) M(T) {}
|
||||
11
test/typeparam/issue54302.dir/main.go
Normal file
11
test/typeparam/issue54302.dir/main.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2022 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 "./a"
|
||||
|
||||
func main() {
|
||||
a.A()
|
||||
}
|
||||
7
test/typeparam/issue54302.go
Normal file
7
test/typeparam/issue54302.go
Normal file
@@ -0,0 +1,7 @@
|
||||
// rundir
|
||||
|
||||
// Copyright 2022 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
|
||||
Reference in New Issue
Block a user