From 99e8f40488b89f3e6c30ddcf94a2c5db61d7ab3d Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 16 Sep 2014 11:43:35 -0400 Subject: runtime: fix 386 build mark finalizer1 as having no pointers TBR=iant CC=golang-codereviews https://golang.org/cl/141570045 --- src/runtime/mgc0.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c index 7a3498ae1e..fda3efcc18 100644 --- a/src/runtime/mgc0.c +++ b/src/runtime/mgc0.c @@ -738,6 +738,7 @@ runtime·gcphasework(G *gp) gp->gcworkdone = true; } +#pragma dataflag NOPTR static byte finalizer1[] = { // Each Finalizer is 5 words, ptr ptr uintptr ptr ptr. // Each byte describes 4 words. -- cgit v1.3 From c1c5d479bd9ead47f718156866c8bd188a8e19b8 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 16 Sep 2014 13:16:43 -0400 Subject: cmd/5g, cmd/8g: make 'out of registers' a fatal error There's no point in continuing. We will only get confused. 6g already makes this fatal. LGTM=dave, minux, iant R=iant, dave, minux CC=golang-codereviews https://golang.org/cl/140660043 --- src/cmd/5g/gsubr.c | 4 ++-- src/cmd/8g/gsubr.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c index 93bfafef66..06e274e14d 100644 --- a/src/cmd/5g/gsubr.c +++ b/src/cmd/5g/gsubr.c @@ -361,7 +361,7 @@ regalloc(Node *n, Type *t, Node *o) print("registers allocated at\n"); for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++) print("%d %p\n", i, regpc[i]); - yyerror("out of fixed registers"); + fatal("out of fixed registers"); goto err; case TFLOAT32: @@ -374,7 +374,7 @@ regalloc(Node *n, Type *t, Node *o) for(i=REGALLOC_F0; i<=REGALLOC_FMAX; i++) if(reg[i] == 0) goto out; - yyerror("out of floating point registers"); + fatal("out of floating point registers"); goto err; case TCOMPLEX64: diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c index a83d048f40..3077e0ad9c 100644 --- a/src/cmd/8g/gsubr.c +++ b/src/cmd/8g/gsubr.c @@ -938,7 +938,7 @@ regalloc(Node *n, Type *t, Node *o) fprint(2, "registers allocated at\n"); for(i=D_AX; i<=D_DI; i++) fprint(2, "\t%R\t%#lux\n", i, regpc[i]); - yyerror("out of fixed registers"); + fatal("out of fixed registers"); goto err; case TFLOAT32: -- cgit v1.3 From 95c899f03c0e19f498ef3b4a4820cf9121e249a1 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 16 Sep 2014 14:02:59 -0400 Subject: net: make TestSelfConnect less fragile We believe TestSelfConnect can accidentally connect to something else listening on or dialing from that port. Fixes #8680. LGTM=bradfitz R=bradfitz CC=golang-codereviews, rlh https://golang.org/cl/136700043 --- src/net/dial_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/net/dial_test.go b/src/net/dial_test.go index c5c3236ccf..19e289f2e5 100644 --- a/src/net/dial_test.go +++ b/src/net/dial_test.go @@ -119,6 +119,7 @@ func TestSelfConnect(t *testing.T) { // TODO(brainman): do not know why it hangs. t.Skip("skipping known-broken test on windows") } + // Test that Dial does not honor self-connects. // See the comment in DialTCP. @@ -149,8 +150,12 @@ func TestSelfConnect(t *testing.T) { for i := 0; i < n; i++ { c, err := DialTimeout("tcp", addr, time.Millisecond) if err == nil { + if c.LocalAddr().String() == addr { + t.Errorf("#%d: Dial %q self-connect", i, addr) + } else { + t.Logf("#%d: Dial %q succeeded - possibly racing with other listener", i, addr) + } c.Close() - t.Errorf("#%d: Dial %q succeeded", i, addr) } } } -- cgit v1.3 From c1e332020ddf1dd31f39d86464136fe06fe6d7fe Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 16 Sep 2014 15:26:00 -0400 Subject: os/signal: increase timeout from 10ms to 100ms Might make test less flaky. Fixes #8682. LGTM=bradfitz R=bradfitz CC=golang-codereviews https://golang.org/cl/143160043 --- src/os/signal/signal_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/os/signal/signal_test.go b/src/os/signal/signal_test.go index 076fe3f93b..22337a72d4 100644 --- a/src/os/signal/signal_test.go +++ b/src/os/signal/signal_test.go @@ -125,7 +125,7 @@ func TestStop(t *testing.T) { if sig != syscall.SIGHUP || *sendUncaughtSighup == 1 { syscall.Kill(syscall.Getpid(), sig) } - time.Sleep(10 * time.Millisecond) + time.Sleep(100 * time.Millisecond) // Ask for signal c := make(chan os.Signal, 1) @@ -140,7 +140,7 @@ func TestStop(t *testing.T) { select { case s := <-c: t.Fatalf("unexpected signal %v", s) - case <-time.After(10 * time.Millisecond): + case <-time.After(100 * time.Millisecond): // nothing to read - good } @@ -154,7 +154,7 @@ func TestStop(t *testing.T) { select { case s := <-c: t.Fatalf("unexpected signal %v", s) - case <-time.After(10 * time.Millisecond): + case <-time.After(100 * time.Millisecond): // nothing to read - good } } -- cgit v1.3 From 15274e5c9bd5393f67a77200f0669ab00f2ab0ed Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Tue, 16 Sep 2014 12:50:05 -0700 Subject: runtime: make it clear that Goexit cannot be recover'd. LGTM=r R=r, bradfitz, khr CC=golang-codereviews https://golang.org/cl/136660044 --- src/runtime/panic.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 12c85e7caf..927b6db44b 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -238,7 +238,8 @@ func deferreturn(arg0 uintptr) { } // Goexit terminates the goroutine that calls it. No other goroutine is affected. -// Goexit runs all deferred calls before terminating the goroutine. +// Goexit runs all deferred calls before terminating the goroutine. Because Goexit +// is not panic, however, any recover calls in those deferred functions will return nil. // // Calling Goexit from the main goroutine terminates that goroutine // without func main returning. Since func main has not returned, -- cgit v1.3 From b22dc6385d66ac2e74afb9a5d503394fc7273d81 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Tue, 16 Sep 2014 14:00:01 -0700 Subject: sync/once: panicking functions still marked as complete This is a corner case, and one that was even tested, but this CL changes the behavior to say that f is "complete" even if it panics. But don't think of it that way, think of it as sync.Once runs the function only the first time it is called, rather than repeatedly until a run of the function completes. Fixes #8118. LGTM=dvyukov R=golang-codereviews, dvyukov CC=golang-codereviews https://golang.org/cl/137350043 --- src/sync/once.go | 7 +++++-- src/sync/once_test.go | 8 ++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/sync/once.go b/src/sync/once.go index 161ae3b3e9..10b42fddc2 100644 --- a/src/sync/once.go +++ b/src/sync/once.go @@ -15,7 +15,7 @@ type Once struct { } // Do calls the function f if and only if Do is being called for the -// first time for this instance of Once. In other words, given +// first time for this instance of Once. In other words, given // var once Once // if once.Do(f) is called multiple times, only the first call will invoke f, // even if f has a different value in each invocation. A new instance of @@ -29,6 +29,9 @@ type Once struct { // Because no call to Do returns until the one call to f returns, if f causes // Do to be called, it will deadlock. // +// If f panics, Do considers it to have returned; future calls of Do return +// without calling f. +// func (o *Once) Do(f func()) { if atomic.LoadUint32(&o.done) == 1 { return @@ -37,7 +40,7 @@ func (o *Once) Do(f func()) { o.m.Lock() defer o.m.Unlock() if o.done == 0 { + defer atomic.StoreUint32(&o.done, 1) f() - atomic.StoreUint32(&o.done, 1) } } diff --git a/src/sync/once_test.go b/src/sync/once_test.go index 8afda82f3e..10beefde35 100644 --- a/src/sync/once_test.go +++ b/src/sync/once_test.go @@ -44,8 +44,12 @@ func TestOncePanic(t *testing.T) { for i := 0; i < 2; i++ { func() { defer func() { - if recover() == nil { - t.Fatalf("Once.Do() has not panic'ed") + r := recover() + if r == nil && i == 0 { + t.Fatalf("Once.Do() has not panic'ed on first iteration") + } + if r != nil && i == 1 { + t.Fatalf("Once.Do() has panic'ed on second iteration") } }() once.Do(func() { -- cgit v1.3 From f1abe0d06bc94399c4abee041624efa36742fc1e Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Tue, 16 Sep 2014 14:22:33 -0700 Subject: sync: simplify TestOncePanic Follow-up to CL 137350043. LGTM=r R=r CC=golang-codereviews https://golang.org/cl/141620043 --- src/sync/once_test.go | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/sync/once_test.go b/src/sync/once_test.go index 10beefde35..1eec8d18ea 100644 --- a/src/sync/once_test.go +++ b/src/sync/once_test.go @@ -40,26 +40,20 @@ func TestOnce(t *testing.T) { } func TestOncePanic(t *testing.T) { - once := new(Once) - for i := 0; i < 2; i++ { - func() { - defer func() { - r := recover() - if r == nil && i == 0 { - t.Fatalf("Once.Do() has not panic'ed on first iteration") - } - if r != nil && i == 1 { - t.Fatalf("Once.Do() has panic'ed on second iteration") - } - }() - once.Do(func() { - panic("failed") - }) + var once Once + func() { + defer func() { + if r := recover(); r == nil { + t.Fatalf("Once.Do did not panic") + } }() - } - once.Do(func() {}) + once.Do(func() { + panic("failed") + }) + }() + once.Do(func() { - t.Fatalf("Once called twice") + t.Fatalf("Once.Do called twice") }) } -- cgit v1.3 From 653fb6d872e31b05441f313911684d5cd351597e Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 16 Sep 2014 17:39:55 -0400 Subject: liblink: make GO_ARGS the default for functions beginning with · MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If there is a leading ·, assume there is a Go prototype and attach the Go prototype information to the function. If the function is not called from Go and does not need a Go prototype, it can be made file-local instead (using name<>(SB)). This fixes the current BSD build failures, by giving functions like sync/atomic.StoreUint32 argument stack map information. Fixes #8753. LGTM=khr, iant R=golang-codereviews, iant, khr, bradfitz CC=golang-codereviews, r, rlh https://golang.org/cl/142150043 --- src/liblink/objfile.c | 23 ++++++++++++++++++++++- src/runtime/asm_386.s | 9 +++------ src/runtime/asm_amd64.s | 9 +++------ src/runtime/asm_arm.s | 9 +++------ src/syscall/asm_darwin_386.s | 5 ----- src/syscall/asm_darwin_amd64.s | 4 ---- src/syscall/asm_dragonfly_386.s | 5 ----- src/syscall/asm_dragonfly_amd64.s | 5 ----- src/syscall/asm_freebsd_386.s | 5 ----- src/syscall/asm_freebsd_amd64.s | 5 ----- src/syscall/asm_freebsd_arm.s | 5 ----- src/syscall/asm_linux_386.s | 7 ------- src/syscall/asm_linux_amd64.s | 5 ----- src/syscall/asm_linux_arm.s | 5 ----- src/syscall/asm_nacl_386.s | 1 - src/syscall/asm_nacl_amd64p32.s | 1 - src/syscall/asm_nacl_arm.s | 1 - src/syscall/asm_netbsd_386.s | 5 ----- src/syscall/asm_netbsd_amd64.s | 5 ----- src/syscall/asm_netbsd_arm.s | 5 ----- src/syscall/asm_openbsd_386.s | 5 ----- src/syscall/asm_openbsd_amd64.s | 5 ----- src/syscall/asm_plan9_386.s | 6 ------ src/syscall/asm_plan9_amd64.s | 6 ------ test/nosplit.go | 13 +++++++++++-- 25 files changed, 42 insertions(+), 112 deletions(-) (limited to 'src') diff --git a/src/liblink/objfile.c b/src/liblink/objfile.c index 02cfae495a..7d4b28c9ac 100644 --- a/src/liblink/objfile.c +++ b/src/liblink/objfile.c @@ -125,7 +125,7 @@ static LSym *rdsym(Link*, Biobuf*, char*); void writeobj(Link *ctxt, Biobuf *b) { - int flag; + int flag, found; Hist *h; LSym *s, *text, *etext, *curtext, *data, *edata; Plist *pl; @@ -251,6 +251,27 @@ writeobj(Link *ctxt, Biobuf *b) s->etext = p; } } + + // Add reference to Go arguments for C or assembly functions without them. + for(s = text; s != nil; s = s->next) { + if(strncmp(s->name, "\"\".", 3) != 0) + continue; + found = 0; + for(p = s->text; p != nil; p = p->link) { + if(p->as == ctxt->arch->AFUNCDATA && p->from.type == ctxt->arch->D_CONST && p->from.offset == FUNCDATA_ArgsPointerMaps) { + found = 1; + break; + } + } + if(!found) { + p = appendp(ctxt, s->text); + p->as = ctxt->arch->AFUNCDATA; + p->from.type = ctxt->arch->D_CONST; + p->from.offset = FUNCDATA_ArgsPointerMaps; + p->to.type = ctxt->arch->D_EXTERN; + p->to.sym = linklookup(ctxt, smprint("%s.args_stackmap", s->name), s->version); + } + } // Turn functions into machine code images. for(s = text; s != nil; s = s->next) { diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index 21065b6d6f..2961f10f2a 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -646,15 +646,13 @@ TEXT gosave<>(SB),NOSPLIT,$0 // Call fn(arg) on the scheduler stack, // aligned appropriately for the gcc ABI. // See cgocall.c for more details. -TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8 - GO_ARGS +TEXT ·asmcgocall(SB),NOSPLIT,$0-8 MOVL fn+0(FP), AX MOVL arg+4(FP), BX CALL asmcgocall<>(SB) RET -TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-12 - GO_ARGS +TEXT ·asmcgocall_errno(SB),NOSPLIT,$0-12 MOVL fn+0(FP), AX MOVL arg+4(FP), BX CALL asmcgocall<>(SB) @@ -714,8 +712,7 @@ TEXT runtime·cgocallback(SB),NOSPLIT,$12-12 // cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize) // See cgocall.c for more details. -TEXT runtime·cgocallback_gofunc(SB),NOSPLIT,$12-12 - GO_ARGS +TEXT ·cgocallback_gofunc(SB),NOSPLIT,$12-12 NO_LOCAL_POINTERS // If g is nil, Go did not create the current thread. diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index da29f61ed8..44159bb57e 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -623,15 +623,13 @@ TEXT gosave<>(SB),NOSPLIT,$0 // Call fn(arg) on the scheduler stack, // aligned appropriately for the gcc ABI. // See cgocall.c for more details. -TEXT runtime·asmcgocall(SB),NOSPLIT,$0-16 - GO_ARGS +TEXT ·asmcgocall(SB),NOSPLIT,$0-16 MOVQ fn+0(FP), AX MOVQ arg+8(FP), BX CALL asmcgocall<>(SB) RET -TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-20 - GO_ARGS +TEXT ·asmcgocall_errno(SB),NOSPLIT,$0-20 MOVQ fn+0(FP), AX MOVQ arg+8(FP), BX CALL asmcgocall<>(SB) @@ -700,8 +698,7 @@ TEXT runtime·cgocallback(SB),NOSPLIT,$24-24 // cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize) // See cgocall.c for more details. -TEXT runtime·cgocallback_gofunc(SB),NOSPLIT,$8-24 - GO_ARGS +TEXT ·cgocallback_gofunc(SB),NOSPLIT,$8-24 NO_LOCAL_POINTERS // If g is nil, Go did not create the current thread. diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s index 3e78d91143..f67f94939b 100644 --- a/src/runtime/asm_arm.s +++ b/src/runtime/asm_arm.s @@ -480,15 +480,13 @@ TEXT gosave<>(SB),NOSPLIT,$0 // Call fn(arg) on the scheduler stack, // aligned appropriately for the gcc ABI. // See cgocall.c for more details. -TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8 - GO_ARGS +TEXT ·asmcgocall(SB),NOSPLIT,$0-8 MOVW fn+0(FP), R1 MOVW arg+4(FP), R0 BL asmcgocall<>(SB) RET -TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-12 - GO_ARGS +TEXT ·asmcgocall_errno(SB),NOSPLIT,$0-12 MOVW fn+0(FP), R1 MOVW arg+4(FP), R0 BL asmcgocall<>(SB) @@ -551,8 +549,7 @@ TEXT runtime·cgocallback(SB),NOSPLIT,$12-12 // cgocallback_gofunc(void (*fn)(void*), void *frame, uintptr framesize) // See cgocall.c for more details. -TEXT runtime·cgocallback_gofunc(SB),NOSPLIT,$8-12 - GO_ARGS +TEXT ·cgocallback_gofunc(SB),NOSPLIT,$8-12 NO_LOCAL_POINTERS // Load m and g from thread-local storage. diff --git a/src/syscall/asm_darwin_386.s b/src/syscall/asm_darwin_386.s index 7d8ddf4378..7205deb12d 100644 --- a/src/syscall/asm_darwin_386.s +++ b/src/syscall/asm_darwin_386.s @@ -17,7 +17,6 @@ // Trap # in AX, args on stack above caller pc. TEXT ·Syscall(SB),NOSPLIT,$0-28 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -42,7 +41,6 @@ ok: RET TEXT ·Syscall6(SB),NOSPLIT,$0-40 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -70,7 +68,6 @@ ok6: RET TEXT ·Syscall9(SB),NOSPLIT,$0-52 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -101,7 +98,6 @@ ok9: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-28 - GO_ARGS MOVL 4(SP), AX // syscall entry // slide args down on top of system call number LEAL 8(SP), SI @@ -123,7 +119,6 @@ ok1: RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 - GO_ARGS MOVL 4(SP), AX // syscall entry // slide args down on top of system call number LEAL 8(SP), SI diff --git a/src/syscall/asm_darwin_amd64.s b/src/syscall/asm_darwin_amd64.s index a3b1bd5346..e57199d2b6 100644 --- a/src/syscall/asm_darwin_amd64.s +++ b/src/syscall/asm_darwin_amd64.s @@ -17,7 +17,6 @@ // Trap # in AX, args in DI SI DX, return in AX DX TEXT ·Syscall(SB),NOSPLIT,$0-56 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 16(SP), DI MOVQ 24(SP), SI @@ -42,7 +41,6 @@ ok: RET TEXT ·Syscall6(SB),NOSPLIT,$0-80 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 16(SP), DI MOVQ 24(SP), SI @@ -67,7 +65,6 @@ ok6: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-56 - GO_ARGS MOVQ 16(SP), DI MOVQ 24(SP), SI MOVQ 32(SP), DX @@ -89,7 +86,6 @@ ok1: RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 - GO_ARGS MOVQ 16(SP), DI MOVQ 24(SP), SI MOVQ 32(SP), DX diff --git a/src/syscall/asm_dragonfly_386.s b/src/syscall/asm_dragonfly_386.s index 0d7d6ba1e7..7012d23c21 100644 --- a/src/syscall/asm_dragonfly_386.s +++ b/src/syscall/asm_dragonfly_386.s @@ -14,7 +14,6 @@ // Trap # in AX, args on stack above caller pc. TEXT ·Syscall(SB),NOSPLIT,$0-32 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -39,7 +38,6 @@ ok: RET TEXT ·Syscall6(SB),NOSPLIT,$0-44 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -67,7 +65,6 @@ ok6: RET TEXT ·Syscall9(SB),NOSPLIT,$0-56 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -98,7 +95,6 @@ ok9: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-32 - GO_ARGS MOVL 4(SP), AX // syscall entry // slide args down on top of system call number LEAL 8(SP), SI @@ -120,7 +116,6 @@ ok1: RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-44 - GO_ARGS MOVL 4(SP), AX // syscall entry // slide args down on top of system call number LEAL 8(SP), SI diff --git a/src/syscall/asm_dragonfly_amd64.s b/src/syscall/asm_dragonfly_amd64.s index b81cf8dda9..004d36089a 100644 --- a/src/syscall/asm_dragonfly_amd64.s +++ b/src/syscall/asm_dragonfly_amd64.s @@ -15,7 +15,6 @@ // Trap # in AX, args in DI SI DX, return in AX DX TEXT ·Syscall(SB),NOSPLIT,$0-64 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 16(SP), DI MOVQ 24(SP), SI @@ -39,7 +38,6 @@ ok: RET TEXT ·Syscall6(SB),NOSPLIT,$0-88 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 16(SP), DI MOVQ 24(SP), SI @@ -63,7 +61,6 @@ ok6: RET TEXT ·Syscall9(SB),NOSPLIT,$0-112 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 8(SP), AX MOVQ 16(SP), DI @@ -97,7 +94,6 @@ ok9: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-64 - GO_ARGS MOVQ 16(SP), DI MOVQ 24(SP), SI MOVQ 32(SP), DX @@ -118,7 +114,6 @@ ok1: RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-88 - GO_ARGS MOVQ 16(SP), DI MOVQ 24(SP), SI MOVQ 32(SP), DX diff --git a/src/syscall/asm_freebsd_386.s b/src/syscall/asm_freebsd_386.s index ff5f9f4a2a..1400d5fea7 100644 --- a/src/syscall/asm_freebsd_386.s +++ b/src/syscall/asm_freebsd_386.s @@ -17,7 +17,6 @@ // Trap # in AX, args on stack above caller pc. TEXT ·Syscall(SB),NOSPLIT,$0-28 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -42,7 +41,6 @@ ok: RET TEXT ·Syscall6(SB),NOSPLIT,$0-40 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -70,7 +68,6 @@ ok6: RET TEXT ·Syscall9(SB),NOSPLIT,$0-52 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -101,7 +98,6 @@ ok9: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-28 - GO_ARGS MOVL 4(SP), AX // syscall entry // slide args down on top of system call number LEAL 8(SP), SI @@ -123,7 +119,6 @@ ok1: RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 - GO_ARGS MOVL 4(SP), AX // syscall entry // slide args down on top of system call number LEAL 8(SP), SI diff --git a/src/syscall/asm_freebsd_amd64.s b/src/syscall/asm_freebsd_amd64.s index 47ceb9287b..c52519098e 100644 --- a/src/syscall/asm_freebsd_amd64.s +++ b/src/syscall/asm_freebsd_amd64.s @@ -23,7 +23,6 @@ // Trap # in AX, args in DI SI DX, return in AX DX TEXT ·Syscall(SB),NOSPLIT,$0-56 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 16(SP), DI MOVQ 24(SP), SI @@ -47,7 +46,6 @@ ok: RET TEXT ·Syscall6(SB),NOSPLIT,$0-80 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 16(SP), DI MOVQ 24(SP), SI @@ -71,7 +69,6 @@ ok6: RET TEXT ·Syscall9(SB),NOSPLIT,$0-104 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 8(SP), AX MOVQ 16(SP), DI @@ -105,7 +102,6 @@ ok9: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-56 - GO_ARGS MOVQ 16(SP), DI MOVQ 24(SP), SI MOVQ 32(SP), DX @@ -126,7 +122,6 @@ ok1: RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 - GO_ARGS MOVQ 16(SP), DI MOVQ 24(SP), SI MOVQ 32(SP), DX diff --git a/src/syscall/asm_freebsd_arm.s b/src/syscall/asm_freebsd_arm.s index ed369ce73e..6b0c182a76 100644 --- a/src/syscall/asm_freebsd_arm.s +++ b/src/syscall/asm_freebsd_arm.s @@ -14,7 +14,6 @@ // func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, errno uintptr) TEXT ·Syscall(SB),NOSPLIT,$0-28 - GO_ARGS BL runtime·entersyscall(SB) MOVW 0(FP), R7 // syscall number MOVW 4(FP), R0 // a1 @@ -37,7 +36,6 @@ error: RET TEXT ·Syscall6(SB),NOSPLIT,$0-40 - GO_ARGS BL runtime·entersyscall(SB) MOVW 0(FP), R7 // syscall number MOVW 4(FP), R0 // a1 @@ -64,7 +62,6 @@ error6: RET TEXT ·Syscall9(SB),NOSPLIT,$0-52 - GO_ARGS BL runtime·entersyscall(SB) MOVW 0(FP), R7 // syscall number MOVW 4(FP), R0 // a1 @@ -91,7 +88,6 @@ error9: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-28 - GO_ARGS MOVW 0(FP), R7 // syscall number MOVW 4(FP), R0 // a1 MOVW 8(FP), R1 // a2 @@ -111,7 +107,6 @@ errorr: RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 - GO_ARGS MOVW 0(FP), R7 // syscall number MOVW 4(FP), R0 // a1 MOVW 8(FP), R1 // a2 diff --git a/src/syscall/asm_linux_386.s b/src/syscall/asm_linux_386.s index 2ce51822da..fa1b371206 100644 --- a/src/syscall/asm_linux_386.s +++ b/src/syscall/asm_linux_386.s @@ -16,7 +16,6 @@ // Trap # in AX, args in BX CX DX SI DI, return in AX TEXT ·Syscall(SB),NOSPLIT,$0-28 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry MOVL 8(SP), BX @@ -42,7 +41,6 @@ ok: // func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr); TEXT ·Syscall6(SB),NOSPLIT,$0-40 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry MOVL 8(SP), BX @@ -69,7 +67,6 @@ ok6: // func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr); TEXT ·RawSyscall(SB),NOSPLIT,$0-28 - GO_ARGS MOVL 4(SP), AX // syscall entry MOVL 8(SP), BX MOVL 12(SP), CX @@ -92,7 +89,6 @@ ok1: // func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr); TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 - GO_ARGS MOVL 4(SP), AX // syscall entry MOVL 8(SP), BX MOVL 12(SP), CX @@ -119,7 +115,6 @@ ok2: // func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int) // Kernel interface gets call sub-number and pointer to a0. TEXT ·socketcall(SB),NOSPLIT,$0-36 - GO_ARGS CALL runtime·entersyscall(SB) MOVL $SYS_SOCKETCALL, AX // syscall entry MOVL 4(SP), BX // socket call number @@ -144,7 +139,6 @@ oksock: // func rawsocketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int) // Kernel interface gets call sub-number and pointer to a0. TEXT ·rawsocketcall(SB),NOSPLIT,$0-36 - GO_ARGS MOVL $SYS_SOCKETCALL, AX // syscall entry MOVL 4(SP), BX // socket call number LEAL 8(SP), CX // pointer to call arguments @@ -170,7 +164,6 @@ oksock1: // Underlying system call is // llseek(int fd, int offhi, int offlo, int64 *result, int whence) TEXT ·seek(SB),NOSPLIT,$0-28 - GO_ARGS CALL runtime·entersyscall(SB) MOVL $SYS__LLSEEK, AX // syscall entry MOVL 4(SP), BX // fd diff --git a/src/syscall/asm_linux_amd64.s b/src/syscall/asm_linux_amd64.s index 0277c506c0..b3ce2165d6 100644 --- a/src/syscall/asm_linux_amd64.s +++ b/src/syscall/asm_linux_amd64.s @@ -18,7 +18,6 @@ // would pass 4th arg in CX, not R10. TEXT ·Syscall(SB),NOSPLIT,$0-56 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 16(SP), DI MOVQ 24(SP), SI @@ -44,7 +43,6 @@ ok: RET TEXT ·Syscall6(SB),NOSPLIT,$0-80 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 16(SP), DI MOVQ 24(SP), SI @@ -70,7 +68,6 @@ ok6: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-56 - GO_ARGS MOVQ 16(SP), DI MOVQ 24(SP), SI MOVQ 32(SP), DX @@ -93,7 +90,6 @@ ok1: RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 - GO_ARGS MOVQ 16(SP), DI MOVQ 24(SP), SI MOVQ 32(SP), DX @@ -116,7 +112,6 @@ ok2: RET TEXT ·gettimeofday(SB),NOSPLIT,$0-16 - GO_ARGS MOVQ 8(SP), DI MOVQ $0, SI MOVQ runtime·__vdso_gettimeofday_sym(SB), AX diff --git a/src/syscall/asm_linux_arm.s b/src/syscall/asm_linux_arm.s index 9451013813..3526533019 100644 --- a/src/syscall/asm_linux_arm.s +++ b/src/syscall/asm_linux_arm.s @@ -14,7 +14,6 @@ // func Syscall(syscall uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr); TEXT ·Syscall(SB),NOSPLIT,$0-28 - GO_ARGS BL runtime·entersyscall(SB) MOVW 4(SP), R7 MOVW 8(SP), R0 @@ -46,7 +45,6 @@ ok: // func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr); // Actually Syscall5 but the rest of the code expects it to be named Syscall6. TEXT ·Syscall6(SB),NOSPLIT,$0-40 - GO_ARGS BL runtime·entersyscall(SB) MOVW 4(SP), R7 // syscall entry MOVW 8(SP), R0 @@ -78,7 +76,6 @@ ok6: // func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr); // Actually RawSyscall5 but the rest of the code expects it to be named RawSyscall6. TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 - GO_ARGS MOVW 4(SP), R7 // syscall entry MOVW 8(SP), R0 MOVW 12(SP), R1 @@ -111,7 +108,6 @@ ok2: // Underlying system call is // llseek(int fd, int offhi, int offlo, int64 *result, int whence) TEXT ·seek(SB),NOSPLIT,$0-32 - GO_ARGS BL runtime·entersyscall(SB) MOVW $SYS__LLSEEK, R7 // syscall entry MOVW 4(SP), R0 // fd @@ -139,7 +135,6 @@ okseek: // func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr); TEXT ·RawSyscall(SB),NOSPLIT,$0-28 - GO_ARGS MOVW 4(SP), R7 // syscall entry MOVW 8(SP), R0 MOVW 12(SP), R1 diff --git a/src/syscall/asm_nacl_386.s b/src/syscall/asm_nacl_386.s index 5352b7697c..cb6fb44166 100644 --- a/src/syscall/asm_nacl_386.s +++ b/src/syscall/asm_nacl_386.s @@ -17,7 +17,6 @@ MOVL $(0x10000 + ((code)<<5)), AX; JMP AX TEXT syscall·Syscall(SB),NOSPLIT,$12-28 - GO_ARGS CALL runtime·entersyscall(SB) MOVL trap+0(FP), AX MOVL a1+4(FP), BX diff --git a/src/syscall/asm_nacl_amd64p32.s b/src/syscall/asm_nacl_amd64p32.s index 637fafab45..72391c431a 100644 --- a/src/syscall/asm_nacl_amd64p32.s +++ b/src/syscall/asm_nacl_amd64p32.s @@ -17,7 +17,6 @@ MOVL $(0x10000 + ((code)<<5)), AX; JMP AX TEXT syscall·Syscall(SB),NOSPLIT,$0-28 - GO_ARGS CALL runtime·entersyscall(SB) MOVL trap+0(FP), AX MOVL a1+4(FP), DI diff --git a/src/syscall/asm_nacl_arm.s b/src/syscall/asm_nacl_arm.s index 3e4479432b..78e10bf7a2 100644 --- a/src/syscall/asm_nacl_arm.s +++ b/src/syscall/asm_nacl_arm.s @@ -17,7 +17,6 @@ MOVW $(0x10000 + ((code)<<5)), R8; B (R8) TEXT syscall·Syscall(SB),NOSPLIT,$0-28 - GO_ARGS BL runtime·entersyscall(SB) MOVW trap+0(FP), R8 MOVW a1+4(FP), R0 diff --git a/src/syscall/asm_netbsd_386.s b/src/syscall/asm_netbsd_386.s index 29442ca9dd..a8c4849f23 100644 --- a/src/syscall/asm_netbsd_386.s +++ b/src/syscall/asm_netbsd_386.s @@ -17,7 +17,6 @@ // Trap # in AX, args on stack above caller pc. TEXT ·Syscall(SB),NOSPLIT,$0-28 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -42,7 +41,6 @@ ok: RET TEXT ·Syscall6(SB),NOSPLIT,$0-40 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -70,7 +68,6 @@ ok6: RET TEXT ·Syscall9(SB),NOSPLIT,$0-52 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -101,7 +98,6 @@ ok9: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-28 - GO_ARGS MOVL 4(SP), AX // syscall entry // slide args down on top of system call number LEAL 8(SP), SI @@ -123,7 +119,6 @@ ok1: RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 - GO_ARGS MOVL 4(SP), AX // syscall entry // slide args down on top of system call number LEAL 8(SP), SI diff --git a/src/syscall/asm_netbsd_amd64.s b/src/syscall/asm_netbsd_amd64.s index 6d0f311f41..b300148f43 100644 --- a/src/syscall/asm_netbsd_amd64.s +++ b/src/syscall/asm_netbsd_amd64.s @@ -18,7 +18,6 @@ // Trap # in AX, args in DI SI DX, return in AX DX TEXT ·Syscall(SB),NOSPLIT,$0-56 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 8(SP), AX // syscall entry MOVQ 16(SP), DI @@ -42,7 +41,6 @@ ok: RET TEXT ·Syscall6(SB),NOSPLIT,$0-80 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 8(SP), AX // syscall entry MOVQ 16(SP), DI @@ -66,7 +64,6 @@ ok6: RET TEXT ·Syscall9(SB),NOSPLIT,$0-104 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 8(SP), AX // syscall entry MOVQ 16(SP), DI @@ -99,7 +96,6 @@ ok9: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-56 - GO_ARGS MOVQ 16(SP), DI MOVQ 24(SP), SI MOVQ 32(SP), DX @@ -120,7 +116,6 @@ ok1: RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 - GO_ARGS MOVQ 16(SP), DI MOVQ 24(SP), SI MOVQ 32(SP), DX diff --git a/src/syscall/asm_netbsd_arm.s b/src/syscall/asm_netbsd_arm.s index c4190ea332..290bb58061 100644 --- a/src/syscall/asm_netbsd_arm.s +++ b/src/syscall/asm_netbsd_arm.s @@ -14,7 +14,6 @@ // func Syscall9(trap int32, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int32) TEXT ·Syscall(SB),NOSPLIT,$0-28 - GO_ARGS BL runtime·entersyscall(SB) MOVW 0(FP), R0 // sigcall num MOVW 4(FP), R1 // a1 @@ -37,7 +36,6 @@ error: RET TEXT ·Syscall6(SB),NOSPLIT,$0-40 - GO_ARGS BL runtime·entersyscall(SB) MOVW 0(FP), R0 // sigcall num MOVW 4(FP), R1 // a1 @@ -63,7 +61,6 @@ error6: RET TEXT ·Syscall9(SB),NOSPLIT,$0-52 - GO_ARGS BL runtime·entersyscall(SB) MOVW 0(FP), R0 // sigcall num MOVW 4(FP), R1 // a1 @@ -89,7 +86,6 @@ error9: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-28 - GO_ARGS MOVW 0(FP), R0 // sigcall num MOVW 4(FP), R1 // a1 MOVW 8(FP), R2 // a2 @@ -109,7 +105,6 @@ errorr: RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 - GO_ARGS MOVW 0(FP), R0 // sigcall num MOVW 4(FP), R1 // a1 MOVW 8(FP), R2 // a2 diff --git a/src/syscall/asm_openbsd_386.s b/src/syscall/asm_openbsd_386.s index e448a70ca1..6458bdf020 100644 --- a/src/syscall/asm_openbsd_386.s +++ b/src/syscall/asm_openbsd_386.s @@ -17,7 +17,6 @@ // Trap # in AX, args on stack above caller pc. TEXT ·Syscall(SB),NOSPLIT,$0-28 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -42,7 +41,6 @@ ok: RET TEXT ·Syscall6(SB),NOSPLIT,$0-40 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -70,7 +68,6 @@ ok6: RET TEXT ·Syscall9(SB),NOSPLIT,$0-52 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -101,7 +98,6 @@ ok9: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-28 - GO_ARGS MOVL 4(SP), AX // syscall entry // slide args down on top of system call number LEAL 8(SP), SI @@ -123,7 +119,6 @@ ok1: RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 - GO_ARGS MOVL 4(SP), AX // syscall entry // slide args down on top of system call number LEAL 8(SP), SI diff --git a/src/syscall/asm_openbsd_amd64.s b/src/syscall/asm_openbsd_amd64.s index 654e3df742..1e981fc01d 100644 --- a/src/syscall/asm_openbsd_amd64.s +++ b/src/syscall/asm_openbsd_amd64.s @@ -18,7 +18,6 @@ // Trap # in AX, args in DI SI DX, return in AX DX TEXT ·Syscall(SB),NOSPLIT,$0-56 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 8(SP), AX // syscall entry MOVQ 16(SP), DI @@ -42,7 +41,6 @@ ok: RET TEXT ·Syscall6(SB),NOSPLIT,$0-80 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 8(SP), AX // syscall entry MOVQ 16(SP), DI @@ -66,7 +64,6 @@ ok6: RET TEXT ·Syscall9(SB),NOSPLIT,$0-104 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 8(SP), AX // syscall entry MOVQ 16(SP), DI @@ -99,7 +96,6 @@ ok9: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-56 - GO_ARGS MOVQ 16(SP), DI MOVQ 24(SP), SI MOVQ 32(SP), DX @@ -120,7 +116,6 @@ ok1: RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 - GO_ARGS MOVQ 16(SP), DI MOVQ 24(SP), SI MOVQ 32(SP), DX diff --git a/src/syscall/asm_plan9_386.s b/src/syscall/asm_plan9_386.s index 46562de845..fc13640b93 100644 --- a/src/syscall/asm_plan9_386.s +++ b/src/syscall/asm_plan9_386.s @@ -19,7 +19,6 @@ // Trap # in AX, args on stack above caller pc. TEXT ·Syscall(SB),NOSPLIT,$0-32 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -55,7 +54,6 @@ copyresult3: RET TEXT ·Syscall6(SB),NOSPLIT,$0-44 - GO_ARGS CALL runtime·entersyscall(SB) MOVL 4(SP), AX // syscall entry // slide args down on top of system call number @@ -94,7 +92,6 @@ copyresult4: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-28 - GO_ARGS MOVL 4(SP), AX // syscall entry // slide args down on top of system call number LEAL 8(SP), SI @@ -110,7 +107,6 @@ TEXT ·RawSyscall(SB),NOSPLIT,$0-28 RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 - GO_ARGS MOVL 4(SP), AX // syscall entry // slide args down on top of system call number LEAL 8(SP), SI @@ -132,7 +128,6 @@ TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 //func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string) TEXT ·seek(SB),NOSPLIT,$0-36 - GO_ARGS LEAL newoffset+24(SP), AX MOVL AX, placeholder+4(SP) @@ -164,7 +159,6 @@ copyresult6: //func exit(code int) // Import runtime·exit for cleanly exiting. TEXT ·exit(SB),NOSPLIT,$4-4 - GO_ARGS NO_LOCAL_POINTERS MOVL code+0(FP), AX MOVL AX, 0(SP) diff --git a/src/syscall/asm_plan9_amd64.s b/src/syscall/asm_plan9_amd64.s index 283e28999a..92419b7172 100644 --- a/src/syscall/asm_plan9_amd64.s +++ b/src/syscall/asm_plan9_amd64.s @@ -18,7 +18,6 @@ //func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) TEXT ·Syscall(SB),NOSPLIT,$0-64 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 8(SP), BP // syscall entry // slide args down on top of system call number @@ -54,7 +53,6 @@ copyresult3: RET TEXT ·Syscall6(SB),NOSPLIT,$0-88 - GO_ARGS CALL runtime·entersyscall(SB) MOVQ 8(SP), BP // syscall entry // slide args down on top of system call number @@ -93,7 +91,6 @@ copyresult4: RET TEXT ·RawSyscall(SB),NOSPLIT,$0-56 - GO_ARGS MOVQ 8(SP), BP // syscall entry // slide args down on top of system call number LEAQ 16(SP), SI @@ -109,7 +106,6 @@ TEXT ·RawSyscall(SB),NOSPLIT,$0-56 RET TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 - GO_ARGS MOVQ 8(SP), BP // syscall entry // slide args down on top of system call number LEAQ 16(SP), SI @@ -131,7 +127,6 @@ TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 //func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string) TEXT ·seek(SB),NOSPLIT,$0-56 - GO_ARGS LEAQ newoffset+40(SP), AX MOVQ AX, placeholder+8(SP) @@ -162,7 +157,6 @@ copyresult6: //func exit(code int) // Import runtime·exit for cleanly exiting. TEXT ·exit(SB),NOSPLIT,$8-8 - GO_ARGS NO_LOCAL_POINTERS MOVQ code+0(FP), AX MOVQ AX, 0(SP) diff --git a/test/nosplit.go b/test/nosplit.go index c9d008acd3..953a5bf0a6 100644 --- a/test/nosplit.go +++ b/test/nosplit.go @@ -12,6 +12,7 @@ import ( "bytes" "fmt" "io/ioutil" + "log" "os" "os/exec" "path/filepath" @@ -190,7 +191,6 @@ func main() { return } defer os.RemoveAll(dir) - ioutil.WriteFile(filepath.Join(dir, "main.go"), []byte("package main\nfunc main()\n"), 0666) tests = strings.Replace(tests, "\t", " ", -1) tests = commentRE.ReplaceAllString(tests, "") @@ -230,6 +230,9 @@ TestCases: continue } + var gobuf bytes.Buffer + fmt.Fprintf(&gobuf, "package main\n") + var buf bytes.Buffer if goarch == "arm" { fmt.Fprintf(&buf, "#define CALL BL\n#define REGISTER (R0)\n") @@ -277,11 +280,17 @@ TestCases: body = callRE.ReplaceAllString(body, "CALL ·$1(SB);") body = callindRE.ReplaceAllString(body, "CALL REGISTER;") + fmt.Fprintf(&gobuf, "func %s()\n", name) fmt.Fprintf(&buf, "TEXT ·%s(SB)%s,$%d-0\n\t%s\n\tRET\n\n", name, nosplit, size, body) } } - ioutil.WriteFile(filepath.Join(dir, "asm.s"), buf.Bytes(), 0666) + if err := ioutil.WriteFile(filepath.Join(dir, "asm.s"), buf.Bytes(), 0666); err != nil { + log.Fatal(err) + } + if err := ioutil.WriteFile(filepath.Join(dir, "main.go"), gobuf.Bytes(), 0666); err != nil { + log.Fatal(err) + } cmd := exec.Command("go", "build") cmd.Dir = dir -- cgit v1.3 From a325f4f2b35d16724750c8c46857d0a840790fd2 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 16 Sep 2014 17:40:10 -0400 Subject: reflect: add Type.Comparable Like most of the Type methods, the definition of Comparable is what the Go spec says it is. Fixes #7911. LGTM=gri R=gri, r CC=golang-codereviews https://golang.org/cl/144020043 --- src/reflect/all_test.go | 38 ++++++++++++++++++++++++++++++++++++++ src/reflect/type.go | 18 +++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 688b5d3107..4be0e353df 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -3185,6 +3185,44 @@ func TestConvert(t *testing.T) { } } +type ComparableStruct struct { + X int +} + +type NonComparableStruct struct { + X int + Y map[string]int +} + +var comparableTests = []struct { + typ Type + ok bool +}{ + {TypeOf(1), true}, + {TypeOf("hello"), true}, + {TypeOf(new(byte)), true}, + {TypeOf((func())(nil)), false}, + {TypeOf([]byte{}), false}, + {TypeOf(map[string]int{}), false}, + {TypeOf(make(chan int)), true}, + {TypeOf(1.5), true}, + {TypeOf(false), true}, + {TypeOf(1i), true}, + {TypeOf(ComparableStruct{}), true}, + {TypeOf(NonComparableStruct{}), false}, + {TypeOf([10]map[string]int{}), false}, + {TypeOf([10]string{}), true}, + {TypeOf(new(interface{})).Elem(), true}, +} + +func TestComparable(t *testing.T) { + for _, tt := range comparableTests { + if ok := tt.typ.Comparable(); ok != tt.ok { + t.Errorf("TypeOf(%v).Comparable() = %v, want %v", tt.typ, ok, tt.ok) + } + } +} + func TestOverflow(t *testing.T) { if ovf := V(float64(0)).OverflowFloat(1e300); ovf { t.Errorf("%v wrongly overflows float64", 1e300) diff --git a/src/reflect/type.go b/src/reflect/type.go index 67818f7f4c..f099546d27 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -96,6 +96,9 @@ type Type interface { // ConvertibleTo returns true if a value of the type is convertible to type u. ConvertibleTo(u Type) bool + // Comparable returns true if values of this type are comparable. + Comparable() bool + // Methods applicable only to some types, depending on Kind. // The methods allowed for each kind are: // @@ -248,7 +251,7 @@ type rtype struct { align uint8 // alignment of variable with this type fieldAlign uint8 // alignment of struct field with this type kind uint8 // enumeration for C - alg *uintptr // algorithm table (../runtime/runtime.h:/Alg) + alg *typeAlg // algorithm table (../runtime/runtime.h:/Alg) gc [2]unsafe.Pointer // garbage collection data string *string // string form; unnecessary but undeniably useful *uncommonType // (relatively) uncommon fields @@ -256,6 +259,15 @@ type rtype struct { zero unsafe.Pointer // pointer to zero value } +type typeAlg struct { + // function for hashing objects of this type + // (ptr to object, size, seed) -> hash + hash func(unsafe.Pointer, uintptr, uintptr) uintptr + // function for comparing objects of this type + // (ptr to object A, ptr to object B, size) -> ==? + equal func(unsafe.Pointer, unsafe.Pointer, uintptr) bool +} + // Method on non-interface type type method struct { name *string // name of method @@ -1096,6 +1108,10 @@ func (t *rtype) ConvertibleTo(u Type) bool { return convertOp(uu, t) != nil } +func (t *rtype) Comparable() bool { + return t.alg != nil && t.alg.equal != nil +} + // implements returns true if the type V implements the interface type T. func implements(T, V *rtype) bool { if T.Kind() != Interface { -- cgit v1.3 From 06e4b06893941b75c3d3955fb6ee75b69e9eb1ae Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 16 Sep 2014 17:40:33 -0400 Subject: net/mail: allow us-ascii encoding Fixes #6611. LGTM=bradfitz R=bradfitz CC=golang-codereviews https://golang.org/cl/14990045 --- src/net/mail/message.go | 13 ++++++++++++- src/net/mail/message_test.go | 10 ++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/net/mail/message.go b/src/net/mail/message.go index ba0778caa7..19aa888d87 100644 --- a/src/net/mail/message.go +++ b/src/net/mail/message.go @@ -28,6 +28,7 @@ import ( "strconv" "strings" "time" + "unicode" ) var debug = debugT(false) @@ -445,7 +446,7 @@ func decodeRFC2047Word(s string) (string, error) { return "", errors.New("address not RFC 2047 encoded") } charset, enc := strings.ToLower(fields[1]), strings.ToLower(fields[2]) - if charset != "iso-8859-1" && charset != "utf-8" { + if charset != "us-ascii" && charset != "iso-8859-1" && charset != "utf-8" { return "", fmt.Errorf("charset not supported: %q", charset) } @@ -466,6 +467,16 @@ func decodeRFC2047Word(s string) (string, error) { } switch charset { + case "us-ascii": + b := new(bytes.Buffer) + for _, c := range dec { + if c >= 0x80 { + b.WriteRune(unicode.ReplacementChar) + } else { + b.WriteRune(rune(c)) + } + } + return b.String(), nil case "iso-8859-1": b := new(bytes.Buffer) for _, c := range dec { diff --git a/src/net/mail/message_test.go b/src/net/mail/message_test.go index eb9c8cbdc9..6ba48be04f 100644 --- a/src/net/mail/message_test.go +++ b/src/net/mail/message_test.go @@ -194,6 +194,16 @@ func TestAddressParsing(t *testing.T) { }, }, }, + // RFC 2047 "Q"-encoded US-ASCII address. Dumb but legal. + { + `=?us-ascii?q?J=6Frg_Doe?= `, + []*Address{ + { + Name: `Jorg Doe`, + Address: "joerg@example.com", + }, + }, + }, // RFC 2047 "Q"-encoded UTF-8 address. { `=?utf-8?q?J=C3=B6rg_Doe?= `, -- cgit v1.3 From 4bf4d9f86ebeb5711464bd98b6823e3e2cca7bb7 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 16 Sep 2014 17:46:25 -0400 Subject: runtime: reenable TestStackGrowth on 32-bit systems If this needs to be turned back off, it should be done just before the '// in finalizer' comment, not at the top of the function. GC is more precise now than it was (the only imprecise stuff left is some global variables), so maybe the finalizer test will work now on 32-bit systems. LGTM=khr R=khr CC=golang-codereviews https://golang.org/cl/144030043 --- src/runtime/stack_test.go | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src') diff --git a/src/runtime/stack_test.go b/src/runtime/stack_test.go index 3a0802a1c2..652c72eeed 100644 --- a/src/runtime/stack_test.go +++ b/src/runtime/stack_test.go @@ -71,10 +71,6 @@ func TestStackMem(t *testing.T) { // Test stack growing in different contexts. func TestStackGrowth(t *testing.T) { - switch GOARCH { - case "386", "arm": - t.Skipf("skipping test on %q; see issue 8083", GOARCH) - } t.Parallel() var wg sync.WaitGroup -- cgit v1.3 From 4caf377e6ef53356414b185930e898acb896c562 Mon Sep 17 00:00:00 2001 From: Ahmed Waheed Moanes Date: Tue, 16 Sep 2014 15:06:52 -0700 Subject: cmd/go: use pkg-config include pathes in swig and don't double compile c++ files. Fixes #8566. LGTM=iant R=iant CC=golang-codereviews https://golang.org/cl/126210045 --- src/cmd/go/build.go | 93 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 45b5bc3233..2e52731529 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -824,12 +824,17 @@ func (b *builder) build(a *action) (err error) { } } - var gofiles, cfiles, sfiles, objects, cgoObjects []string + var gofiles, cfiles, sfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string gofiles = append(gofiles, a.p.GoFiles...) cfiles = append(cfiles, a.p.CFiles...) sfiles = append(sfiles, a.p.SFiles...) + if a.p.usesCgo() || a.p.usesSwig() { + if pcCFLAGS, pcLDFLAGS, err = b.getPkgConfigFlags(a.p); err != nil { + return + } + } // Run cgo. if a.p.usesCgo() { // In a package using cgo, cgo compiles the C, C++ and assembly files with gcc. @@ -860,7 +865,7 @@ func (b *builder) build(a *action) (err error) { if a.cgo != nil && a.cgo.target != "" { cgoExe = a.cgo.target } - outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles, a.p.CXXFiles, a.p.MFiles) + outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, gccfiles, a.p.CXXFiles, a.p.MFiles) if err != nil { return err } @@ -873,9 +878,18 @@ func (b *builder) build(a *action) (err error) { // In a package using SWIG, any .c or .s files are // compiled with gcc. gccfiles := append(cfiles, sfiles...) + cxxfiles, mfiles := a.p.CXXFiles, a.p.MFiles cfiles = nil sfiles = nil - outGo, outObj, err := b.swig(a.p, obj, gccfiles, a.p.CXXFiles, a.p.MFiles) + + // Don't build c/c++ files twice if cgo is enabled (mainly for pkg-config). + if a.p.usesCgo() { + cxxfiles = nil + gccfiles = nil + mfiles = nil + } + + outGo, outObj, err := b.swig(a.p, obj, pcCFLAGS, gccfiles, cxxfiles, mfiles) if err != nil { return err } @@ -1019,6 +1033,34 @@ func (b *builder) build(a *action) (err error) { return nil } +// Calls pkg-config if needed and returns the cflags/ldflags needed to build the package. +func (b *builder) getPkgConfigFlags(p *Package) (cflags, ldflags []string, err error) { + if pkgs := p.CgoPkgConfig; len(pkgs) > 0 { + var out []byte + out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--cflags", pkgs) + if err != nil { + b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out)) + b.print(err.Error() + "\n") + err = errPrintedOutput + return + } + if len(out) > 0 { + cflags = strings.Fields(string(out)) + } + out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--libs", pkgs) + if err != nil { + b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out)) + b.print(err.Error() + "\n") + err = errPrintedOutput + return + } + if len(out) > 0 { + ldflags = strings.Fields(string(out)) + } + } + return +} + // install is the action for installing a single package or executable. func (b *builder) install(a *action) (err error) { defer func() { @@ -2100,36 +2142,16 @@ var ( cgoLibGccFileOnce sync.Once ) -func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { +func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := b.cflags(p, true) _, cgoexeCFLAGS, _, _ := b.cflags(p, false) - + cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...) + cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...) // If we are compiling Objective-C code, then we need to link against libobjc if len(mfiles) > 0 { cgoLDFLAGS = append(cgoLDFLAGS, "-lobjc") } - if pkgs := p.CgoPkgConfig; len(pkgs) > 0 { - out, err := b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--cflags", pkgs) - if err != nil { - b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out)) - b.print(err.Error() + "\n") - return nil, nil, errPrintedOutput - } - if len(out) > 0 { - cgoCPPFLAGS = append(cgoCPPFLAGS, strings.Fields(string(out))...) - } - out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--libs", pkgs) - if err != nil { - b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out)) - b.print(err.Error() + "\n") - return nil, nil, errPrintedOutput - } - if len(out) > 0 { - cgoLDFLAGS = append(cgoLDFLAGS, strings.Fields(string(out))...) - } - } - // Allows including _cgo_export.h from .[ch] files in the package. cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", obj) @@ -2344,7 +2366,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles // Run SWIG on all SWIG input files. // TODO: Don't build a shared library, once SWIG emits the necessary // pragmas for external linking. -func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { +func (b *builder) swig(p *Package, obj string, pcCFLAGS, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true) cflags := stringList(cgoCPPFLAGS, cgoCFLAGS) cxxflags := stringList(cgoCPPFLAGS, cgoCXXFLAGS) @@ -2385,7 +2407,7 @@ func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles, mfiles []stri } for _, f := range p.SwigFiles { - goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, false, intgosize) + goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, pcCFLAGS, false, intgosize) if err != nil { return nil, nil, err } @@ -2400,7 +2422,7 @@ func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles, mfiles []stri } } for _, f := range p.SwigCXXFiles { - goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, true, intgosize) + goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, pcCFLAGS, true, intgosize) if err != nil { return nil, nil, err } @@ -2479,13 +2501,13 @@ func (b *builder) swigIntSize(obj string) (intsize string, err error) { } // Run SWIG on one SWIG input file. -func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize string) (outGo, outObj, objGccObj string, err error) { +func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outObj, objGccObj string, err error) { cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true) var cflags []string if cxx { - cflags = stringList(cgoCPPFLAGS, cgoCXXFLAGS) + cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCXXFLAGS) } else { - cflags = stringList(cgoCPPFLAGS, cgoCFLAGS) + cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCFLAGS) } n := 5 // length of ".swig" @@ -2511,6 +2533,13 @@ func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize stri "-o", obj + gccBase + gccExt, "-outdir", obj, } + + for _, f := range cflags { + if len(f) > 3 && f[:2] == "-I" { + args = append(args, f) + } + } + if gccgo { args = append(args, "-gccgo") if pkgpath := gccgoPkgpath(p); pkgpath != "" { -- cgit v1.3 From da8cf5438aa676a99e8bb55c94011b2581743e1a Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Tue, 16 Sep 2014 17:26:16 -0700 Subject: runtime: always run semacquire on the G stack semacquire might need to park the currently running G. It can only park if called from the G stack (because it has no way of saving the M stack state). So all calls to semacquire must come from the G stack. The three violators are GOMAXPROCS, ReadMemStats, and WriteHeapDump. This change moves the semacquire call earlier, out of their C code and into their Go code. This seldom caused bugs because semacquire seldom actually had to park the caller. But it did happen intermittently. Fixes #8749 LGTM=dvyukov R=golang-codereviews, dvyukov, bradfitz CC=golang-codereviews https://golang.org/cl/144940043 --- src/runtime/debug.go | 30 ++++++++++++++----- src/runtime/heapdump.c | 25 +--------------- src/runtime/mem.go | 37 ++++++++++++++++++++++- src/runtime/mgc0.c | 26 +---------------- src/runtime/proc.c | 79 ++++---------------------------------------------- src/runtime/runtime.h | 39 +++++++++++++++++++++++++ src/runtime/sema.go | 5 ++++ src/runtime/stubs.go | 2 ++ src/runtime/thunk.s | 3 ++ 9 files changed, 114 insertions(+), 132 deletions(-) (limited to 'src') diff --git a/src/runtime/debug.go b/src/runtime/debug.go index bb4bd60ed4..4414dd55d2 100644 --- a/src/runtime/debug.go +++ b/src/runtime/debug.go @@ -24,15 +24,29 @@ func UnlockOSThread() // The number of logical CPUs on the local machine can be queried with NumCPU. // This call will go away when the scheduler improves. func GOMAXPROCS(n int) int { - g := getg() - g.m.scalararg[0] = uintptr(n) - onM(gomaxprocs_m) - n = int(g.m.scalararg[0]) - g.m.scalararg[0] = 0 - return n -} + if n > _MaxGomaxprocs { + n = _MaxGomaxprocs + } + lock(&sched.lock) + ret := int(gomaxprocs) + unlock(&sched.lock) + if n <= 0 || n == ret { + return ret + } -func gomaxprocs_m() // proc.c + semacquire(&worldsema, false) + gp := getg() + gp.m.gcing = 1 + onM(stoptheworld) + + // newprocs will be processed by starttheworld + newprocs = int32(n) + + gp.m.gcing = 0 + semrelease(&worldsema) + onM(starttheworld) + return ret +} // NumCPU returns the number of logical CPUs on the local machine. func NumCPU() int { diff --git a/src/runtime/heapdump.c b/src/runtime/heapdump.c index 8bbc7d8a56..75897c3d35 100644 --- a/src/runtime/heapdump.c +++ b/src/runtime/heapdump.c @@ -737,33 +737,16 @@ mdump(void) flush(); } -static void writeheapdump_m(void); - -#pragma textflag NOSPLIT void -runtime∕debug·WriteHeapDump(uintptr fd) -{ - void (*fn)(void); - - g->m->scalararg[0] = fd; - fn = writeheapdump_m; - runtime·onM(&fn); -} - -static void -writeheapdump_m(void) +runtime·writeheapdump_m(void) { uintptr fd; fd = g->m->scalararg[0]; g->m->scalararg[0] = 0; - // Stop the world. runtime·casgstatus(g->m->curg, Grunning, Gwaiting); g->waitreason = runtime·gostringnocopy((byte*)"dumping heap"); - runtime·semacquire(&runtime·worldsema, false); - g->m->gcing = 1; - runtime·stoptheworld(); // Update stats so we can dump them. // As a side effect, flushes all the MCaches so the MSpan.freelist @@ -784,13 +767,7 @@ writeheapdump_m(void) tmpbufsize = 0; } - // Start up the world again. - g->m->gcing = 0; - g->m->locks++; - runtime·semrelease(&runtime·worldsema); - runtime·starttheworld(); runtime·casgstatus(g->m->curg, Gwaiting, Grunning); - g->m->locks--; } // dumpint() the kind & offset of each field in an object. diff --git a/src/runtime/mem.go b/src/runtime/mem.go index 34391b2eb2..99bb928511 100644 --- a/src/runtime/mem.go +++ b/src/runtime/mem.go @@ -69,4 +69,39 @@ func init() { } // ReadMemStats populates m with memory allocator statistics. -func ReadMemStats(m *MemStats) +func ReadMemStats(m *MemStats) { + // Have to acquire worldsema to stop the world, + // because stoptheworld can only be used by + // one goroutine at a time, and there might be + // a pending garbage collection already calling it. + semacquire(&worldsema, false) + gp := getg() + gp.m.gcing = 1 + onM(stoptheworld) + + gp.m.ptrarg[0] = noescape(unsafe.Pointer(m)) + onM(readmemstats_m) + + gp.m.gcing = 0 + gp.m.locks++ + semrelease(&worldsema) + onM(starttheworld) + gp.m.locks-- +} + +// Implementation of runtime/debug.WriteHeapDump +func writeHeapDump(fd uintptr) { + semacquire(&worldsema, false) + gp := getg() + gp.m.gcing = 1 + onM(stoptheworld) + + gp.m.scalararg[0] = fd + onM(writeheapdump_m) + + gp.m.gcing = 0 + gp.m.locks++ + semrelease(&worldsema) + onM(starttheworld) + gp.m.locks-- +} diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c index fda3efcc18..35aed78a53 100644 --- a/src/runtime/mgc0.c +++ b/src/runtime/mgc0.c @@ -1451,32 +1451,14 @@ extern uintptr runtime·sizeof_C_MStats; static void readmemstats_m(void); -#pragma textflag NOSPLIT void -runtime·ReadMemStats(MStats *stats) -{ - void (*fn)(void); - - g->m->ptrarg[0] = stats; - fn = readmemstats_m; - runtime·onM(&fn); -} - -static void -readmemstats_m(void) +runtime·readmemstats_m(void) { MStats *stats; stats = g->m->ptrarg[0]; g->m->ptrarg[0] = nil; - // Have to acquire worldsema to stop the world, - // because stoptheworld can only be used by - // one goroutine at a time, and there might be - // a pending garbage collection already calling it. - runtime·semacquire(&runtime·worldsema, false); - g->m->gcing = 1; - runtime·stoptheworld(); runtime·updatememstats(nil); // Size of the trailing by_size array differs between Go and C, // NumSizeClasses was changed, but we can not change Go struct because of backward compatibility. @@ -1486,12 +1468,6 @@ readmemstats_m(void) stats->stacks_sys = stats->stacks_inuse; stats->heap_inuse -= stats->stacks_inuse; stats->heap_sys -= stats->stacks_inuse; - - g->m->gcing = 0; - g->m->locks++; - runtime·semrelease(&runtime·worldsema); - runtime·starttheworld(); - g->m->locks--; } static void readgcstats_m(void); diff --git a/src/runtime/proc.c b/src/runtime/proc.c index 25f9166403..0e677a9d28 100644 --- a/src/runtime/proc.c +++ b/src/runtime/proc.c @@ -24,42 +24,6 @@ // // Design doc at http://golang.org/s/go11sched. -typedef struct Sched Sched; -struct Sched { - Mutex lock; - - uint64 goidgen; - - M* midle; // idle m's waiting for work - int32 nmidle; // number of idle m's waiting for work - int32 nmidlelocked; // number of locked m's waiting for work - int32 mcount; // number of m's that have been created - int32 maxmcount; // maximum number of m's allowed (or die) - - P* pidle; // idle P's - uint32 npidle; - uint32 nmspinning; - - // Global runnable queue. - G* runqhead; - G* runqtail; - int32 runqsize; - - // Global cache of dead G's. - Mutex gflock; - G* gfree; - int32 ngfree; - - uint32 gcwaiting; // gc is waiting to run - int32 stopwait; - Note stopnote; - uint32 sysmonwait; - Note sysmonnote; - uint64 lastpoll; - - int32 profilehz; // cpu profiling rate -}; - enum { // Number of goroutine ids to grab from runtime·sched.goidgen to local per-P cache at once. @@ -67,7 +31,7 @@ enum GoidCacheBatch = 16, }; -Sched runtime·sched; +SchedType runtime·sched; int32 runtime·gomaxprocs; uint32 runtime·needextram; bool runtime·iscgo; @@ -79,7 +43,7 @@ M* runtime·extram; P* runtime·allp[MaxGomaxprocs+1]; int8* runtime·goos; int32 runtime·ncpu; -static int32 newprocs; +int32 runtime·newprocs; Mutex runtime·allglock; // the following vars are protected by this lock or by stoptheworld G** runtime·allg; @@ -763,9 +727,9 @@ runtime·starttheworld(void) injectglist(gp); add = needaddgcproc(); runtime·lock(&runtime·sched.lock); - if(newprocs) { - procresize(newprocs); - newprocs = 0; + if(runtime·newprocs) { + procresize(runtime·newprocs); + runtime·newprocs = 0; } else procresize(runtime·gomaxprocs); runtime·sched.gcwaiting = 0; @@ -2364,39 +2328,6 @@ runtime·Breakpoint(void) runtime·breakpoint(); } -// Implementation of runtime.GOMAXPROCS. -// delete when scheduler is even stronger -void -runtime·gomaxprocs_m(void) -{ - int32 n, ret; - - n = g->m->scalararg[0]; - g->m->scalararg[0] = 0; - - if(n > MaxGomaxprocs) - n = MaxGomaxprocs; - runtime·lock(&runtime·sched.lock); - ret = runtime·gomaxprocs; - if(n <= 0 || n == ret) { - runtime·unlock(&runtime·sched.lock); - g->m->scalararg[0] = ret; - return; - } - runtime·unlock(&runtime·sched.lock); - - runtime·semacquire(&runtime·worldsema, false); - g->m->gcing = 1; - runtime·stoptheworld(); - newprocs = n; - g->m->gcing = 0; - runtime·semrelease(&runtime·worldsema); - runtime·starttheworld(); - - g->m->scalararg[0] = ret; - return; -} - // lockOSThread is called by runtime.LockOSThread and runtime.lockOSThread below // after they modify m->locked. Do not allow preemption during this call, // or else the m might be different in this function than in the caller. diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index adc74cf417..c034f3aa97 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -60,6 +60,7 @@ typedef struct SudoG SudoG; typedef struct Mutex Mutex; typedef struct M M; typedef struct P P; +typedef struct SchedType SchedType; typedef struct Note Note; typedef struct Slice Slice; typedef struct String String; @@ -433,6 +434,42 @@ enum { MaxGomaxprocs = 1<<8, }; +struct SchedType +{ + Mutex lock; + + uint64 goidgen; + + M* midle; // idle m's waiting for work + int32 nmidle; // number of idle m's waiting for work + int32 nmidlelocked; // number of locked m's waiting for work + int32 mcount; // number of m's that have been created + int32 maxmcount; // maximum number of m's allowed (or die) + + P* pidle; // idle P's + uint32 npidle; + uint32 nmspinning; + + // Global runnable queue. + G* runqhead; + G* runqtail; + int32 runqsize; + + // Global cache of dead G's. + Mutex gflock; + G* gfree; + int32 ngfree; + + uint32 gcwaiting; // gc is waiting to run + int32 stopwait; + Note stopnote; + uint32 sysmonwait; + Note sysmonnote; + uint64 lastpoll; + + int32 profilehz; // cpu profiling rate +}; + // The m->locked word holds two pieces of state counting active calls to LockOSThread/lockOSThread. // The low bit (LockExternal) is a boolean reporting whether any LockOSThread call is active. // External locks are not recursive; a second lock is silently ignored. @@ -716,6 +753,8 @@ extern DebugVars runtime·debug; extern uintptr runtime·maxstacksize; extern Note runtime·signote; extern ForceGCState runtime·forcegc; +extern SchedType runtime·sched; +extern int32 runtime·newprocs; /* * common functions and data diff --git a/src/runtime/sema.go b/src/runtime/sema.go index beacd67162..504462de33 100644 --- a/src/runtime/sema.go +++ b/src/runtime/sema.go @@ -49,6 +49,11 @@ func asyncsemrelease(addr *uint32) { // Called from runtime. func semacquire(addr *uint32, profile bool) { + gp := getg() + if gp != gp.m.curg { + gothrow("semacquire not on the G stack") + } + // Easy case. if cansemacquire(addr) { return diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go index ff443c4cd4..2e6aadca7a 100644 --- a/src/runtime/stubs.go +++ b/src/runtime/stubs.go @@ -119,6 +119,8 @@ func deferproc_m() func goexit_m() func startpanic_m() func dopanic_m() +func readmemstats_m() +func writeheapdump_m() // memclr clears n bytes starting at ptr. // in memclr_*.s diff --git a/src/runtime/thunk.s b/src/runtime/thunk.s index 3b66cf47d3..3dd86e9919 100644 --- a/src/runtime/thunk.s +++ b/src/runtime/thunk.s @@ -80,6 +80,9 @@ TEXT reflect·memmove(SB), NOSPLIT, $0-0 TEXT runtime∕debug·freeOSMemory(SB), NOSPLIT, $0-0 JMP runtime·freeOSMemory(SB) +TEXT runtime∕debug·WriteHeapDump(SB), NOSPLIT, $0-0 + JMP runtime·writeHeapDump(SB) + TEXT net·runtime_pollServerInit(SB),NOSPLIT,$0-0 JMP runtime·netpollServerInit(SB) -- cgit v1.3 From cbf97d9103b2bbfb8c798f06c751e74093062b57 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 16 Sep 2014 20:53:38 -0400 Subject: liblink, sync/atomic: fix arm build The liblink code to insert the FUNCDATA for a stack map from the Go prototype was not correct for ARM (different data structure layout). Also, sync/atomic was missing some Go prototypes for ARM-specific functions. TBR=r CC=golang-codereviews https://golang.org/cl/143160045 --- include/link.h | 1 + src/liblink/obj5.c | 1 + src/liblink/objfile.c | 7 ++++++- src/sync/atomic/64bit_arm.go | 6 ++++++ 4 files changed, 14 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/include/link.h b/include/link.h index 8c73eab51e..292b077394 100644 --- a/include/link.h +++ b/include/link.h @@ -471,6 +471,7 @@ struct LinkArch int D_PARAM; int D_SCONST; int D_STATIC; + int D_OREG; int ACALL; int ADATA; diff --git a/src/liblink/obj5.c b/src/liblink/obj5.c index a571d8f166..e192b082b5 100644 --- a/src/liblink/obj5.c +++ b/src/liblink/obj5.c @@ -1061,6 +1061,7 @@ LinkArch linkarm = { .D_PARAM = D_PARAM, .D_SCONST = D_SCONST, .D_STATIC = D_STATIC, + .D_OREG = D_OREG, .ACALL = ABL, .ADATA = ADATA, diff --git a/src/liblink/objfile.c b/src/liblink/objfile.c index 7d4b28c9ac..9b1e1b7a8f 100644 --- a/src/liblink/objfile.c +++ b/src/liblink/objfile.c @@ -268,7 +268,12 @@ writeobj(Link *ctxt, Biobuf *b) p->as = ctxt->arch->AFUNCDATA; p->from.type = ctxt->arch->D_CONST; p->from.offset = FUNCDATA_ArgsPointerMaps; - p->to.type = ctxt->arch->D_EXTERN; + if(ctxt->arch->thechar == '6' || ctxt->arch->thechar == '8') + p->to.type = ctxt->arch->D_EXTERN; + else { + p->to.type = ctxt->arch->D_OREG; + p->to.name = ctxt->arch->D_EXTERN; + } p->to.sym = linklookup(ctxt, smprint("%s.args_stackmap", s->name), s->version); } } diff --git a/src/sync/atomic/64bit_arm.go b/src/sync/atomic/64bit_arm.go index c08f214c7e..0aab7160e9 100644 --- a/src/sync/atomic/64bit_arm.go +++ b/src/sync/atomic/64bit_arm.go @@ -44,3 +44,9 @@ func swapUint64(addr *uint64, new uint64) (old uint64) { } return } + +// Additional ARM-specific assembly routines. +// Declaration here to give assembly routines correct stack maps for arguments. +func armCompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool) +func armCompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool) +func generalCAS64(addr *uint64, old, new uint64) (swapped bool) -- cgit v1.3 From 98a1e207e29359dba86769877021838bb77b12c3 Mon Sep 17 00:00:00 2001 From: Dmitriy Vyukov Date: Tue, 16 Sep 2014 19:54:26 -0700 Subject: sync/atomic: add Value A Value provides an atomic load and store of a consistently typed value. It's intended to be used with copy-on-write idiom (see the example). Performance: BenchmarkValueRead 50000000 21.7 ns/op BenchmarkValueRead-2 200000000 8.63 ns/op BenchmarkValueRead-4 300000000 4.33 ns/op TBR=rsc R=golang-codereviews CC=golang-codereviews https://golang.org/cl/136710045 --- src/runtime/thunk.s | 6 ++ src/sync/atomic/norace.go | 17 ++++ src/sync/atomic/race.go | 22 +++++ src/sync/atomic/value.go | 91 ++++++++++++++++++++ src/sync/atomic/value_test.go | 195 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 331 insertions(+) create mode 100644 src/sync/atomic/norace.go create mode 100644 src/sync/atomic/race.go create mode 100644 src/sync/atomic/value.go create mode 100644 src/sync/atomic/value_test.go (limited to 'src') diff --git a/src/runtime/thunk.s b/src/runtime/thunk.s index 3dd86e9919..5e8e674f54 100644 --- a/src/runtime/thunk.s +++ b/src/runtime/thunk.s @@ -166,3 +166,9 @@ TEXT runtime·main_main(SB),NOSPLIT,$0-0 TEXT runtime·timenow(SB), NOSPLIT, $0-0 JMP time·now(SB) + +TEXT sync∕atomic·runtime_procPin(SB),NOSPLIT,$0-0 + JMP sync·runtime_procPin(SB) + +TEXT sync∕atomic·runtime_procUnpin(SB),NOSPLIT,$0-0 + JMP sync·runtime_procUnpin(SB) diff --git a/src/sync/atomic/norace.go b/src/sync/atomic/norace.go new file mode 100644 index 0000000000..1866fd16cb --- /dev/null +++ b/src/sync/atomic/norace.go @@ -0,0 +1,17 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !race + +package atomic + +import "unsafe" + +const raceenabled = false + +func raceAcquire(addr unsafe.Pointer) { +} + +func raceReleaseMerge(addr unsafe.Pointer) { +} diff --git a/src/sync/atomic/race.go b/src/sync/atomic/race.go new file mode 100644 index 0000000000..a833d9e7f4 --- /dev/null +++ b/src/sync/atomic/race.go @@ -0,0 +1,22 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build race + +package atomic + +import ( + "runtime" + "unsafe" +) + +const raceenabled = true + +func raceAcquire(addr unsafe.Pointer) { + runtime.RaceAcquire(addr) +} + +func raceReleaseMerge(addr unsafe.Pointer) { + runtime.RaceReleaseMerge(addr) +} diff --git a/src/sync/atomic/value.go b/src/sync/atomic/value.go new file mode 100644 index 0000000000..c290fdab85 --- /dev/null +++ b/src/sync/atomic/value.go @@ -0,0 +1,91 @@ +// Copyright 2014 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 atomic + +import ( + "unsafe" +) + +// A Value provides an atomic load and store of a consistently typed value. +// Values can be created as part of other data structures. +// The zero value for a Value returns nil from Load. +// Once Store has been called, a Value must not be copied. +type Value struct { + v interface{} +} + +// ifaceWords is interface{} internal representation. +type ifaceWords struct { + typ unsafe.Pointer + data unsafe.Pointer +} + +// Load returns the value set by the most recent Store. +// It returns nil if there has been no call to Store for this Value. +func (v *Value) Load() (x interface{}) { + vp := (*ifaceWords)(unsafe.Pointer(v)) + typ := LoadPointer(&vp.typ) + if typ == nil || uintptr(typ) == ^uintptr(0) { + // First store not yet completed. + return nil + } + data := LoadPointer(&vp.data) + xp := (*ifaceWords)(unsafe.Pointer(&x)) + xp.typ = typ + xp.data = data + if raceenabled { + raceAcquire(unsafe.Pointer(v)) + } + return +} + +// Store sets the value of the Value to v. +// All calls to Store for a given Value must use values of the same concrete type. +// Store of an inconsistent type panics, as does Store(nil). +func (v *Value) Store(x interface{}) { + if x == nil { + panic("sync/atomic: store of nil value into Value") + } + if raceenabled { + raceReleaseMerge(unsafe.Pointer(v)) + } + vp := (*ifaceWords)(unsafe.Pointer(v)) + xp := (*ifaceWords)(unsafe.Pointer(&x)) + for { + typ := LoadPointer(&vp.typ) + if typ == nil { + // Attempt to start first store. + // Disable preemption so that other goroutines can use + // active spin wait to wait for completion; and so that + // GC does not see the fake type accidentally. + runtime_procPin() + if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) { + runtime_procUnpin() + continue + } + // Complete first store. + StorePointer(&vp.data, xp.data) + StorePointer(&vp.typ, xp.typ) + runtime_procUnpin() + return + } + if uintptr(typ) == ^uintptr(0) { + // First store in progress. Wait. + // Since we disable preemption around the first store, + // we can wait with active spinning. + continue + } + // First store completed. Check type and overwrite data. + if typ != xp.typ { + panic("sync/atomic: store of inconsistently typed value into Value") + } + StorePointer(&vp.data, xp.data) + return + } +} + +// Disable/enable preemption, implemented in runtime. +func runtime_procPin() +func runtime_procUnpin() diff --git a/src/sync/atomic/value_test.go b/src/sync/atomic/value_test.go new file mode 100644 index 0000000000..382dc6854d --- /dev/null +++ b/src/sync/atomic/value_test.go @@ -0,0 +1,195 @@ +// Copyright 2014 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 atomic_test + +import ( + "math/rand" + "runtime" + "sync" + . "sync/atomic" + "testing" + "time" +) + +func TestValue(t *testing.T) { + var v Value + if v.Load() != nil { + t.Fatal("initial Value is not nil") + } + v.Store(42) + x := v.Load() + if xx, ok := x.(int); !ok || xx != 42 { + t.Fatalf("wrong value: got %+v, want 42", x) + } + v.Store(84) + x = v.Load() + if xx, ok := x.(int); !ok || xx != 84 { + t.Fatalf("wrong value: got %+v, want 84", x) + } +} + +func TestValueLarge(t *testing.T) { + var v Value + v.Store("foo") + x := v.Load() + if xx, ok := x.(string); !ok || xx != "foo" { + t.Fatalf("wrong value: got %+v, want foo", x) + } + v.Store("barbaz") + x = v.Load() + if xx, ok := x.(string); !ok || xx != "barbaz" { + t.Fatalf("wrong value: got %+v, want barbaz", x) + } +} + +func TestValuePanic(t *testing.T) { + const nilErr = "sync/atomic: store of nil value into Value" + const badErr = "sync/atomic: store of inconsistently typed value into Value" + var v Value + func() { + defer func() { + err := recover() + if err != nilErr { + t.Fatalf("inconsistent store panic: got '%v', want '%v'", err, nilErr) + } + }() + v.Store(nil) + }() + v.Store(42) + func() { + defer func() { + err := recover() + if err != badErr { + t.Fatalf("inconsistent store panic: got '%v', want '%v'", err, badErr) + } + }() + v.Store("foo") + }() + func() { + defer func() { + err := recover() + if err != nilErr { + t.Fatalf("inconsistent store panic: got '%v', want '%v'", err, nilErr) + } + }() + v.Store(nil) + }() +} + +func TestValueConcurrent(t *testing.T) { + tests := [][]interface{}{ + {uint16(0), ^uint16(0), uint16(1 + 2<<8), uint16(3 + 4<<8)}, + {uint32(0), ^uint32(0), uint32(1 + 2<<16), uint32(3 + 4<<16)}, + {uint64(0), ^uint64(0), uint64(1 + 2<<32), uint64(3 + 4<<32)}, + {complex(0, 0), complex(1, 2), complex(3, 4), complex(5, 6)}, + } + p := 4 * runtime.GOMAXPROCS(0) + for _, test := range tests { + var v Value + done := make(chan bool) + for i := 0; i < p; i++ { + go func() { + r := rand.New(rand.NewSource(rand.Int63())) + loop: + for j := 0; j < 1e5; j++ { + x := test[r.Intn(len(test))] + v.Store(x) + x = v.Load() + for _, x1 := range test { + if x == x1 { + continue loop + } + } + t.Logf("loaded unexpected value %+v, want %+v", x, test) + done <- false + } + done <- true + }() + } + for i := 0; i < p; i++ { + if !<-done { + t.FailNow() + } + } + } +} + +func BenchmarkValueRead(b *testing.B) { + var v Value + v.Store(new(int)) + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + x := v.Load().(*int) + if *x != 0 { + b.Fatalf("wrong value: got %v, want 0", *x) + } + } + }) +} + +// The following example shows how to use Value for periodic program config updates +// and propagation of the changes to worker goroutines. +func ExampleValue_config() { + var config Value // holds current server configuration + // Create initial config value and store into config. + config.Store(loadConfig()) + go func() { + // Reload config every 10 seconds + // and update config value with the new version. + for { + time.Sleep(10 * time.Second) + config.Store(loadConfig()) + } + }() + // Create worker goroutines that handle incoming requests + // using the latest config value. + for i := 0; i < 10; i++ { + go func() { + for r := range requests() { + c := config.Load() + // Handle request r using config c. + _, _ = r, c + } + }() + } +} + +func loadConfig() map[string]string { + return make(map[string]string) +} + +func requests() chan int { + return make(chan int) +} + +// The following example shows how to maintain a scalable frequently read, +// but infrequently updated data structure using copy-on-write idiom. +func ExampleValue_readMostly() { + type Map map[string]string + var m Value + m.Store(make(Map)) + var mu sync.Mutex // used only by writers + // read function can be used to read the data without further synchronization + read := func(key string) (val string) { + m1 := m.Load().(Map) + return m1[key] + } + // insert function can be used to update the data without further synchronization + insert := func(key, val string) { + mu.Lock() // synchronize with other potential writers + defer mu.Unlock() + m1 := m.Load().(Map) // load current value of the data structure + m2 := make(Map) // create a new value + for k, v := range m1 { + m2[k] = v // copy all data from the current object to the new one + } + m2[key] = val // do the update that we need + m.Store(m2) // atomically replace the current object with the new one + // At this point all new readers start working with the new version. + // The old version will be garbage collected once the existing readers + // (if any) are done with it. + } + _, _ = read, insert +} -- cgit v1.3 From 72a2539c38d06be486b180310d5703700f9f1f5f Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Wed, 17 Sep 2014 05:44:42 +0000 Subject: sync: fix linux/arm build For real this time. LGTM=minux R=golang-codereviews, minux CC=golang-codereviews https://golang.org/cl/141640043 --- src/sync/atomic/export_linux_arm_test.go | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/sync/atomic/export_linux_arm_test.go b/src/sync/atomic/export_linux_arm_test.go index 5cd43353ee..9f0c856a79 100644 --- a/src/sync/atomic/export_linux_arm_test.go +++ b/src/sync/atomic/export_linux_arm_test.go @@ -4,6 +4,4 @@ package atomic -func generalCAS64(addr *uint64, old uint64, new uint64) bool - var GeneralCAS64 = generalCAS64 -- cgit v1.3 From 6c934238c93f8f60775409f1ab410ce9c9ea2357 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Wed, 17 Sep 2014 08:32:15 -0700 Subject: runtime: change minimum stack size to 2K. It will be 8K on windows because it needs 4K for the OS. Similarly, plan9 will be 4K. On linux/amd64, reduces size of 100,000 goroutines from ~819MB to ~245MB. Update #7514 LGTM=dvyukov R=golang-codereviews, dvyukov, khr, aram CC=golang-codereviews https://golang.org/cl/145790043 --- src/runtime/proc.c | 8 ++++---- src/runtime/stack.h | 21 ++++++++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/runtime/proc.c b/src/runtime/proc.c index 0e677a9d28..4282a145e1 100644 --- a/src/runtime/proc.c +++ b/src/runtime/proc.c @@ -2162,11 +2162,11 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp siz = narg + nret; siz = (siz+7) & ~7; - // We could instead create a secondary stack frame - // and make it look like goexit was on the original but - // the call to the actual goroutine function was split. + // We could allocate a larger initial stack if necessary. // Not worth it: this is almost always an error. - if(siz > StackMin - 1024) + // 4*sizeof(uintreg): extra space added below + // sizeof(uintreg): caller's LR (arm) or return address (x86, in gostartcall). + if(siz >= StackMin - 4*sizeof(uintreg) - sizeof(uintreg)) runtime·throw("runtime.newproc: function arguments too large for new goroutine"); p = g->m->p; diff --git a/src/runtime/stack.h b/src/runtime/stack.h index b30e322166..f97dc4ed8d 100644 --- a/src/runtime/stack.h +++ b/src/runtime/stack.h @@ -69,16 +69,19 @@ enum { #endif // Plan 9 #endif // Windows - // The amount of extra stack to allocate beyond the size - // needed for the single frame that triggered the split. - StackExtra = 2048, + // The minimum size of stack used by Go code + StackMin = 2048, - // The minimum stack segment size to allocate. - // If the amount needed for the splitting frame + StackExtra - // is less than this number, the stack will have this size instead. - StackMin = 8192, - StackSystemRounded = StackSystem + (-StackSystem & (StackMin-1)), - FixedStack = StackMin + StackSystemRounded, + // The minimum stack size to allocate. + // The hackery here rounds FixedStack0 up to a power of 2. + FixedStack0 = StackMin + StackSystem, + FixedStack1 = FixedStack0 - 1, + FixedStack2 = FixedStack1 | (FixedStack1 >> 1), + FixedStack3 = FixedStack2 | (FixedStack2 >> 2), + FixedStack4 = FixedStack3 | (FixedStack3 >> 4), + FixedStack5 = FixedStack4 | (FixedStack4 >> 8), + FixedStack6 = FixedStack5 | (FixedStack5 >> 16), + FixedStack = FixedStack6 + 1, // Functions that need frames bigger than this use an extra // instruction to do the stack split check, to avoid overflow -- cgit v1.3 From e19d8a47d1803a19446c658712c4bdff84d0da31 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 17 Sep 2014 14:49:32 -0400 Subject: runtime: account for tiny allocs, for testing.AllocsPerRun Fixes #8734. LGTM=r, bradfitz, dvyukov R=bradfitz, r, dvyukov CC=golang-codereviews, iant, khr https://golang.org/cl/143150043 --- src/runtime/malloc.c | 5 ++++- src/runtime/malloc.go | 2 +- src/runtime/malloc.h | 3 +++ src/runtime/mem.go | 2 +- src/runtime/mgc0.c | 1 + src/runtime/mheap.c | 4 ++++ src/testing/allocs_test.go | 29 +++++++++++++++++++++++++++++ 7 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/testing/allocs_test.go (limited to 'src') diff --git a/src/runtime/malloc.c b/src/runtime/malloc.c index cfb698ac21..d5f2b9ab80 100644 --- a/src/runtime/malloc.c +++ b/src/runtime/malloc.c @@ -79,6 +79,8 @@ runtime·purgecachedstats(MCache *c) h = &runtime·mheap; mstats.heap_alloc += c->local_cachealloc; c->local_cachealloc = 0; + mstats.tinyallocs += c->local_tinyallocs; + c->local_tinyallocs = 0; mstats.nlookup += c->local_nlookup; c->local_nlookup = 0; h->largefree += c->local_largefree; @@ -92,9 +94,10 @@ runtime·purgecachedstats(MCache *c) } // Size of the trailing by_size array differs between Go and C, +// and all data after by_size is local to C, not exported to Go. // NumSizeClasses was changed, but we can not change Go struct because of backward compatibility. // sizeof_C_MStats is what C thinks about size of Go struct. -uintptr runtime·sizeof_C_MStats = sizeof(MStats) - (NumSizeClasses - 61) * sizeof(mstats.by_size[0]); +uintptr runtime·sizeof_C_MStats = offsetof(MStats, by_size[61]); #define MaxArena32 (2U<<30) diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go index acf6b48f84..fc22cc29e4 100644 --- a/src/runtime/malloc.go +++ b/src/runtime/malloc.go @@ -103,7 +103,6 @@ func mallocgc(size uintptr, typ *_type, flags int) unsafe.Pointer { // standalone escaping variables. On a json benchmark // the allocator reduces number of allocations by ~12% and // reduces heap size by ~20%. - tinysize := uintptr(c.tinysize) if size <= tinysize { tiny := unsafe.Pointer(c.tiny) @@ -121,6 +120,7 @@ func mallocgc(size uintptr, typ *_type, flags int) unsafe.Pointer { x = tiny c.tiny = (*byte)(add(x, size)) c.tinysize -= uintptr(size1) + c.local_tinyallocs++ if debugMalloc { mp := acquirem() if mp.mallocing == 0 { diff --git a/src/runtime/malloc.h b/src/runtime/malloc.h index 3f1981f708..410a007173 100644 --- a/src/runtime/malloc.h +++ b/src/runtime/malloc.h @@ -278,6 +278,8 @@ struct MStats uint64 nmalloc; uint64 nfree; } by_size[NumSizeClasses]; + + uint64 tinyallocs; // number of tiny allocations that didn't cause actual allocation; not exported to Go directly }; #define mstats runtime·memstats @@ -331,6 +333,7 @@ struct MCache // See "Tiny allocator" comment in malloc.goc. byte* tiny; uintptr tinysize; + uintptr local_tinyallocs; // number of tiny allocs not counted in other stats // The rest is not accessed on every malloc. MSpan* alloc[NumSizeClasses]; // spans to allocate from diff --git a/src/runtime/mem.go b/src/runtime/mem.go index 99bb928511..b3c216f18e 100644 --- a/src/runtime/mem.go +++ b/src/runtime/mem.go @@ -64,7 +64,7 @@ func init() { var memStats MemStats if sizeof_C_MStats != unsafe.Sizeof(memStats) { println(sizeof_C_MStats, unsafe.Sizeof(memStats)) - panic("MStats vs MemStatsType size mismatch") + gothrow("MStats vs MemStatsType size mismatch") } } diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c index 35aed78a53..4e901726f6 100644 --- a/src/runtime/mgc0.c +++ b/src/runtime/mgc0.c @@ -1245,6 +1245,7 @@ runtime·updatememstats(GCStats *stats) mstats.by_size[i].nmalloc += runtime·mheap.nsmallfree[i]; smallfree += runtime·mheap.nsmallfree[i] * runtime·class_to_size[i]; } + mstats.nfree += mstats.tinyallocs; mstats.nmalloc += mstats.nfree; // Calculate derived stats. diff --git a/src/runtime/mheap.c b/src/runtime/mheap.c index 902a5c71a2..bb203d5ce5 100644 --- a/src/runtime/mheap.c +++ b/src/runtime/mheap.c @@ -184,6 +184,8 @@ mheap_alloc(MHeap *h, uintptr npage, int32 sizeclass, bool large) // transfer stats from cache to global mstats.heap_alloc += g->m->mcache->local_cachealloc; g->m->mcache->local_cachealloc = 0; + mstats.tinyallocs += g->m->mcache->local_tinyallocs; + g->m->mcache->local_tinyallocs = 0; s = MHeap_AllocSpanLocked(h, npage); if(s != nil) { @@ -465,6 +467,8 @@ mheap_free(MHeap *h, MSpan *s, int32 acct) runtime·lock(&h->lock); mstats.heap_alloc += g->m->mcache->local_cachealloc; g->m->mcache->local_cachealloc = 0; + mstats.tinyallocs += g->m->mcache->local_tinyallocs; + g->m->mcache->local_tinyallocs = 0; if(acct) { mstats.heap_alloc -= s->npages< Date: Wed, 17 Sep 2014 15:30:48 -0400 Subject: sync/atomic: add more missing Go prototype Should fix nacl/arm build. TBR=minux CC=golang-codereviews https://golang.org/cl/145070043 --- src/sync/atomic/64bit_arm.go | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/sync/atomic/64bit_arm.go b/src/sync/atomic/64bit_arm.go index 0aab7160e9..b98e60827e 100644 --- a/src/sync/atomic/64bit_arm.go +++ b/src/sync/atomic/64bit_arm.go @@ -50,3 +50,9 @@ func swapUint64(addr *uint64, new uint64) (old uint64) { func armCompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool) func armCompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool) func generalCAS64(addr *uint64, old, new uint64) (swapped bool) +func armAddUint32(addr *uint32, delta uint32) (new uint32) +func armAddUint64(addr *uint64, delta uint64) (new uint64) +func armSwapUint32(addr *uint32, new uint32) (old uint32) +func armSwapUint64(addr *uint64, new uint64) (old uint64) +func armLoadUint64(addr *uint64) (val uint64) +func armStoreUint64(addr *uint64, val uint64) -- cgit v1.3 From 6fb9d50d154c4a8ac4db11e91a3d221322859191 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 17 Sep 2014 16:12:17 -0400 Subject: runtime: print more detail in adjustframe crash The logic here is copied from mgc0.c's scanframe. Mostly it is messages although the minsize code is new (and I believe necessary). I am hoping to get more information about the current arm build failures (or, if it's the minsize thing, fix them). TBR=khr R=khr CC=golang-codereviews https://golang.org/cl/143180043 --- src/runtime/stack.c | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/runtime/stack.c b/src/runtime/stack.c index 143b645e42..b38ee31d48 100644 --- a/src/runtime/stack.c +++ b/src/runtime/stack.c @@ -463,7 +463,7 @@ adjustframe(Stkframe *frame, void *arg) StackMap *stackmap; int32 pcdata; BitVector bv; - uintptr targetpc; + uintptr targetpc, size, minsize; adjinfo = arg; targetpc = frame->continpc; @@ -486,27 +486,47 @@ adjustframe(Stkframe *frame, void *arg) if(pcdata == -1) pcdata = 0; // in prologue - // adjust local pointers - if((byte*)frame->varp != (byte*)frame->sp) { + // Adjust local variables if stack frame has been allocated. + size = frame->varp - frame->sp; + if(thechar != '6' && thechar != '8') + minsize = sizeof(uintptr); + else + minsize = 0; + if(size > minsize) { stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps); - if(stackmap == nil) - runtime·throw("no locals info"); - if(stackmap->n <= 0) - runtime·throw("locals size info only"); + if(stackmap == nil || stackmap->n <= 0) { + runtime·printf("runtime: frame %s untyped locals %p+%p\n", runtime·funcname(f), (byte*)(frame->varp-size), size); + runtime·throw("missing stackmap"); + } + // Locals bitmap information, scan just the pointers in locals. + if(pcdata < 0 || pcdata >= stackmap->n) { + // don't know where we are + runtime·printf("runtime: pcdata is %d and %d locals stack map entries for %s (targetpc=%p)\n", + pcdata, stackmap->n, runtime·funcname(f), targetpc); + runtime·throw("bad symbol table"); + } bv = runtime·stackmapdata(stackmap, pcdata); + size = (bv.n * PtrSize) / BitsPerPointer; if(StackDebug >= 3) runtime·printf(" locals\n"); - adjustpointers((byte**)frame->varp - bv.n / BitsPerPointer, &bv, adjinfo, f); + adjustpointers((byte**)(frame->varp - size), &bv, adjinfo, f); } - // adjust inargs and outargs - if(frame->arglen != 0) { + + // Adjust arguments. + if(frame->arglen > 0) { if(frame->argmap != nil) { bv = *frame->argmap; } else { stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps); - if(stackmap == nil) { - runtime·printf("size %d\n", (int32)frame->arglen); - runtime·throw("no arg info"); + if(stackmap == nil || stackmap->n <= 0) { + runtime·printf("runtime: frame %s untyped args %p+%p\n", runtime·funcname(f), frame->argp, (uintptr)frame->arglen); + runtime·throw("missing stackmap"); + } + if(pcdata < 0 || pcdata >= stackmap->n) { + // don't know where we are + runtime·printf("runtime: pcdata is %d and %d args stack map entries for %s (targetpc=%p)\n", + pcdata, stackmap->n, runtime·funcname(f), targetpc); + runtime·throw("bad symbol table"); } bv = runtime·stackmapdata(stackmap, pcdata); } -- cgit v1.3 From 92eb1e1600c3770d2ec669a8d6b7947cac551305 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Wed, 17 Sep 2014 13:25:46 -0700 Subject: runtime: free stacks of Gdead goroutines at GC time We could probably free the G structures as well, but for the allg list. Leaving that for another day. Fixes #8287 LGTM=rsc R=golang-codereviews, dvyukov, khr, rsc CC=golang-codereviews https://golang.org/cl/145010043 --- src/runtime/stack.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/runtime/stack.c b/src/runtime/stack.c index b38ee31d48..95a5a123d9 100644 --- a/src/runtime/stack.c +++ b/src/runtime/stack.c @@ -806,8 +806,16 @@ runtime·shrinkstack(G *gp) { uintptr used, oldsize, newsize; - if(runtime·readgstatus(gp) == Gdead) + if(runtime·readgstatus(gp) == Gdead) { + if(gp->stack.lo != 0) { + // Free whole stack - it will get reallocated + // if G is used again. + runtime·stackfree(gp->stack); + gp->stack.lo = 0; + gp->stack.hi = 0; + } return; + } if(gp->stack.lo == 0) runtime·throw("missing stack in shrinkstack"); -- cgit v1.3 From a2910958a2bcc360e811d1f6f476784b213bb0f1 Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Thu, 18 Sep 2014 12:43:01 +1000 Subject: image/gif: fix GIF encoding of sub-images. benchmark old ns/op new ns/op delta BenchmarkEncode 8641055 8646829 +0.07% Fixes #7792. LGTM=r R=r CC=dbathgate, golang-codereviews https://golang.org/cl/147730043 --- src/image/gif/writer.go | 18 ++++++++++++++---- src/image/gif/writer_test.go | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/image/gif/writer.go b/src/image/gif/writer.go index 15cd40fadf..49abde704c 100644 --- a/src/image/gif/writer.go +++ b/src/image/gif/writer.go @@ -233,10 +233,20 @@ func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) { e.writeByte(uint8(litWidth)) // LZW Minimum Code Size. lzww := lzw.NewWriter(blockWriter{e: e}, lzw.LSB, litWidth) - _, e.err = lzww.Write(pm.Pix) - if e.err != nil { - lzww.Close() - return + if dx := b.Dx(); dx == pm.Stride { + _, e.err = lzww.Write(pm.Pix) + if e.err != nil { + lzww.Close() + return + } + } else { + for i, y := 0, b.Min.Y; y < b.Max.Y; i, y = i+pm.Stride, y+1 { + _, e.err = lzww.Write(pm.Pix[i : i+dx]) + if e.err != nil { + lzww.Close() + return + } + } } lzww.Close() e.writeByte(0x00) // Block Terminator. diff --git a/src/image/gif/writer_test.go b/src/image/gif/writer_test.go index bc5d37845b..93306ffdb3 100644 --- a/src/image/gif/writer_test.go +++ b/src/image/gif/writer_test.go @@ -102,6 +102,29 @@ func TestWriter(t *testing.T) { } } +func TestSubImage(t *testing.T) { + m0, err := readImg("../testdata/video-001.gif") + if err != nil { + t.Fatalf("readImg: %v", err) + } + m0 = m0.(*image.Paletted).SubImage(image.Rect(0, 0, 50, 30)) + var buf bytes.Buffer + err = Encode(&buf, m0, nil) + if err != nil { + t.Fatalf("Encode: %v", err) + } + m1, err := Decode(&buf) + if err != nil { + t.Fatalf("Decode: %v", err) + } + if m0.Bounds() != m1.Bounds() { + t.Fatalf("bounds differ: %v and %v", m0.Bounds(), m1.Bounds()) + } + if averageDelta(m0, m1) != 0 { + t.Fatalf("images differ") + } +} + var frames = []string{ "../testdata/video-001.gif", "../testdata/video-005.gray.gif", -- cgit v1.3 From ed7db89b90409c1ef0be44f4e5cea7dd6475ba4e Mon Sep 17 00:00:00 2001 From: Dmitriy Vyukov Date: Wed, 17 Sep 2014 20:38:50 -0700 Subject: runtime: output number of goroutines in GC trace Example output: gc26(1): 1+0+1038+0 us, 0 -> 1 MB, 19074 (88777-69703) objects, 5 gs, 184/0/0 sweeps, 0(0) handoff, 0(0) steal, 0/0/0 yields It's useful to understand long scan times, because goroutine traceback is slow. LGTM=khr R=golang-codereviews, khr CC=golang-codereviews, rsc https://golang.org/cl/136310044 --- src/runtime/mgc0.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c index 4e901726f6..88c8d0f3c9 100644 --- a/src/runtime/mgc0.c +++ b/src/runtime/mgc0.c @@ -1401,11 +1401,13 @@ gc(struct gc_args *args) stats.nsleep += work.markfor->nsleep; runtime·printf("gc%d(%d): %D+%D+%D+%D us, %D -> %D MB, %D (%D-%D) objects," + " %d goroutines," " %d/%d/%d sweeps," " %D(%D) handoff, %D(%D) steal, %D/%D/%D yields\n", mstats.numgc, work.nproc, (t1-t0)/1000, (t2-t1)/1000, (t3-t2)/1000, (t4-t3)/1000, heap0>>20, heap1>>20, obj, mstats.nmalloc, mstats.nfree, + runtime·gcount(), work.nspan, runtime·sweep.nbgsweep, runtime·sweep.npausesweep, stats.nhandoff, stats.nhandoffcnt, work.markfor->nsteal, work.markfor->nstealcnt, -- cgit v1.3 From 8c2484ec11d27324423e3cf27cc9ac6b34394c7d Mon Sep 17 00:00:00 2001 From: Dmitriy Vyukov Date: Wed, 17 Sep 2014 21:22:11 -0700 Subject: sync/atomic: remove unnecessary race instrumentation in Value It is left from the time when Value was implemented in assembly. Now it is implemented in Go and race detector understands Go. In particular the atomic operations must provide all necessary synchronization. LGTM=adg R=golang-codereviews, adg CC=golang-codereviews, khr, rsc https://golang.org/cl/145880043 --- src/sync/atomic/norace.go | 17 ----------------- src/sync/atomic/race.go | 22 ---------------------- src/sync/atomic/value.go | 6 ------ 3 files changed, 45 deletions(-) delete mode 100644 src/sync/atomic/norace.go delete mode 100644 src/sync/atomic/race.go (limited to 'src') diff --git a/src/sync/atomic/norace.go b/src/sync/atomic/norace.go deleted file mode 100644 index 1866fd16cb..0000000000 --- a/src/sync/atomic/norace.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !race - -package atomic - -import "unsafe" - -const raceenabled = false - -func raceAcquire(addr unsafe.Pointer) { -} - -func raceReleaseMerge(addr unsafe.Pointer) { -} diff --git a/src/sync/atomic/race.go b/src/sync/atomic/race.go deleted file mode 100644 index a833d9e7f4..0000000000 --- a/src/sync/atomic/race.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build race - -package atomic - -import ( - "runtime" - "unsafe" -) - -const raceenabled = true - -func raceAcquire(addr unsafe.Pointer) { - runtime.RaceAcquire(addr) -} - -func raceReleaseMerge(addr unsafe.Pointer) { - runtime.RaceReleaseMerge(addr) -} diff --git a/src/sync/atomic/value.go b/src/sync/atomic/value.go index c290fdab85..ab46d9a240 100644 --- a/src/sync/atomic/value.go +++ b/src/sync/atomic/value.go @@ -35,9 +35,6 @@ func (v *Value) Load() (x interface{}) { xp := (*ifaceWords)(unsafe.Pointer(&x)) xp.typ = typ xp.data = data - if raceenabled { - raceAcquire(unsafe.Pointer(v)) - } return } @@ -48,9 +45,6 @@ func (v *Value) Store(x interface{}) { if x == nil { panic("sync/atomic: store of nil value into Value") } - if raceenabled { - raceReleaseMerge(unsafe.Pointer(v)) - } vp := (*ifaceWords)(unsafe.Pointer(v)) xp := (*ifaceWords)(unsafe.Pointer(&x)) for { -- cgit v1.3 From 484cc6715192efd4bfea57647eeb66c93aecfb0c Mon Sep 17 00:00:00 2001 From: Mikio Hara Date: Thu, 18 Sep 2014 19:17:55 +0900 Subject: net: separate NaCl dependent placeholders from BSD's To clarify the dependency of NaCl platform. LGTM=adg R=golang-codereviews, adg CC=golang-codereviews https://golang.org/cl/143830044 --- src/net/dial_test.go | 5 +++++ src/net/dnsclient_unix.go | 2 +- src/net/dnsconfig_unix.go | 2 +- src/net/file_stub.go | 38 +++++++++++++++++++++++++++++++++ src/net/file_test.go | 2 +- src/net/file_unix.go | 2 +- src/net/ipraw_test.go | 5 +++++ src/net/lookup_stub.go | 49 +++++++++++++++++++++++++++++++++++++++++++ src/net/lookup_unix.go | 2 +- src/net/port_test.go | 6 ++++++ src/net/port_unix.go | 2 +- src/net/sock_bsd.go | 2 +- src/net/sock_solaris.go | 13 ------------ src/net/sock_stub.go | 15 +++++++++++++ src/net/sockopt_bsd.go | 2 +- src/net/sockopt_posix.go | 2 +- src/net/sockopt_stub.go | 37 ++++++++++++++++++++++++++++++++ src/net/sockoptip_bsd.go | 2 +- src/net/sockoptip_posix.go | 2 +- src/net/sockoptip_stub.go | 14 ++++++------- src/net/tcpsockopt_openbsd.go | 16 ++++++++++++++ src/net/tcpsockopt_posix.go | 2 +- src/net/tcpsockopt_stub.go | 8 ++++--- src/os/exec/exec_test.go | 5 +++-- 24 files changed, 198 insertions(+), 37 deletions(-) create mode 100644 src/net/file_stub.go create mode 100644 src/net/lookup_stub.go delete mode 100644 src/net/sock_solaris.go create mode 100644 src/net/sock_stub.go create mode 100644 src/net/sockopt_stub.go create mode 100644 src/net/tcpsockopt_openbsd.go (limited to 'src') diff --git a/src/net/dial_test.go b/src/net/dial_test.go index 19e289f2e5..abeb500098 100644 --- a/src/net/dial_test.go +++ b/src/net/dial_test.go @@ -465,6 +465,11 @@ func TestDialer(t *testing.T) { } func TestDialDualStackLocalhost(t *testing.T) { + switch runtime.GOOS { + case "nacl": + t.Skipf("skipping test on %q", runtime.GOOS) + } + if ips, err := LookupIP("localhost"); err != nil { t.Fatalf("LookupIP failed: %v", err) } else if len(ips) < 2 || !supportsIPv4 || !supportsIPv6 { diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go index abe7da05cb..7511083f79 100644 --- a/src/net/dnsclient_unix.go +++ b/src/net/dnsclient_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build darwin dragonfly freebsd linux netbsd openbsd solaris // DNS client: see RFC 1035. // Has to be linked into package net for Dial. diff --git a/src/net/dnsconfig_unix.go b/src/net/dnsconfig_unix.go index ebb6e673f1..66ab7c4dd3 100644 --- a/src/net/dnsconfig_unix.go +++ b/src/net/dnsconfig_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build darwin dragonfly freebsd linux netbsd openbsd solaris // Read system DNS config from /etc/resolv.conf diff --git a/src/net/file_stub.go b/src/net/file_stub.go new file mode 100644 index 0000000000..4281072ef9 --- /dev/null +++ b/src/net/file_stub.go @@ -0,0 +1,38 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build nacl + +package net + +import ( + "os" + "syscall" +) + +// FileConn returns a copy of the network connection corresponding to +// the open file f. It is the caller's responsibility to close f when +// finished. Closing c does not affect f, and closing f does not +// affect c. +func FileConn(f *os.File) (c Conn, err error) { + return nil, syscall.ENOPROTOOPT + +} + +// FileListener returns a copy of the network listener corresponding +// to the open file f. It is the caller's responsibility to close l +// when finished. Closing l does not affect f, and closing f does not +// affect l. +func FileListener(f *os.File) (l Listener, err error) { + return nil, syscall.ENOPROTOOPT + +} + +// FilePacketConn returns a copy of the packet network connection +// corresponding to the open file f. It is the caller's +// responsibility to close f when finished. Closing c does not affect +// f, and closing f does not affect c. +func FilePacketConn(f *os.File) (c PacketConn, err error) { + return nil, syscall.ENOPROTOOPT +} diff --git a/src/net/file_test.go b/src/net/file_test.go index d81bca7824..6fab06a9c6 100644 --- a/src/net/file_test.go +++ b/src/net/file_test.go @@ -89,7 +89,7 @@ var fileListenerTests = []struct { func TestFileListener(t *testing.T) { switch runtime.GOOS { - case "windows": + case "nacl", "windows": t.Skipf("skipping test on %q", runtime.GOOS) } diff --git a/src/net/file_unix.go b/src/net/file_unix.go index 07b3ecf626..214a4196c8 100644 --- a/src/net/file_unix.go +++ b/src/net/file_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build darwin dragonfly freebsd linux netbsd openbsd solaris package net diff --git a/src/net/ipraw_test.go b/src/net/ipraw_test.go index 0632dafc65..92dc8dc569 100644 --- a/src/net/ipraw_test.go +++ b/src/net/ipraw_test.go @@ -68,6 +68,11 @@ func skipRawSocketTest(t *testing.T) (skip bool, skipmsg string) { } func TestResolveIPAddr(t *testing.T) { + switch runtime.GOOS { + case "nacl": + t.Skipf("skipping test on %q", runtime.GOOS) + } + for _, tt := range resolveIPAddrTests { addr, err := ResolveIPAddr(tt.net, tt.litAddrOrName) if err != tt.err { diff --git a/src/net/lookup_stub.go b/src/net/lookup_stub.go new file mode 100644 index 0000000000..502aafb270 --- /dev/null +++ b/src/net/lookup_stub.go @@ -0,0 +1,49 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build nacl + +package net + +import "syscall" + +func lookupProtocol(name string) (proto int, err error) { + return 0, syscall.ENOPROTOOPT +} + +func lookupHost(host string) (addrs []string, err error) { + return nil, syscall.ENOPROTOOPT +} + +func lookupIP(host string) (ips []IP, err error) { + return nil, syscall.ENOPROTOOPT +} + +func lookupPort(network, service string) (port int, err error) { + return 0, syscall.ENOPROTOOPT +} + +func lookupCNAME(name string) (cname string, err error) { + return "", syscall.ENOPROTOOPT +} + +func lookupSRV(service, proto, name string) (cname string, srvs []*SRV, err error) { + return "", nil, syscall.ENOPROTOOPT +} + +func lookupMX(name string) (mxs []*MX, err error) { + return nil, syscall.ENOPROTOOPT +} + +func lookupNS(name string) (nss []*NS, err error) { + return nil, syscall.ENOPROTOOPT +} + +func lookupTXT(name string) (txts []string, err error) { + return nil, syscall.ENOPROTOOPT +} + +func lookupAddr(addr string) (ptrs []string, err error) { + return nil, syscall.ENOPROTOOPT +} diff --git a/src/net/lookup_unix.go b/src/net/lookup_unix.go index b1d2f8f31a..a54578456d 100644 --- a/src/net/lookup_unix.go +++ b/src/net/lookup_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build darwin dragonfly freebsd linux netbsd openbsd solaris package net diff --git a/src/net/port_test.go b/src/net/port_test.go index 9e8968f359..4811ade69e 100644 --- a/src/net/port_test.go +++ b/src/net/port_test.go @@ -5,6 +5,7 @@ package net import ( + "runtime" "testing" ) @@ -43,6 +44,11 @@ var porttests = []portTest{ } func TestLookupPort(t *testing.T) { + switch runtime.GOOS { + case "nacl": + t.Skipf("skipping test on %q", runtime.GOOS) + } + for i := 0; i < len(porttests); i++ { tt := porttests[i] if port, err := LookupPort(tt.netw, tt.name); port != tt.port || (err == nil) != tt.ok { diff --git a/src/net/port_unix.go b/src/net/port_unix.go index 89558c1f02..348c771c35 100644 --- a/src/net/port_unix.go +++ b/src/net/port_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build darwin dragonfly freebsd linux netbsd openbsd solaris // Read system port mappings from /etc/services diff --git a/src/net/sock_bsd.go b/src/net/sock_bsd.go index 48fb785275..6c37109f5e 100644 --- a/src/net/sock_bsd.go +++ b/src/net/sock_bsd.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd nacl netbsd openbsd +// +build darwin dragonfly freebsd netbsd openbsd package net diff --git a/src/net/sock_solaris.go b/src/net/sock_solaris.go deleted file mode 100644 index 90fe9de894..0000000000 --- a/src/net/sock_solaris.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2009 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 net - -import "syscall" - -func maxListenerBacklog() int { - // TODO: Implement this - // NOTE: Never return a number bigger than 1<<16 - 1. See issue 5030. - return syscall.SOMAXCONN -} diff --git a/src/net/sock_stub.go b/src/net/sock_stub.go new file mode 100644 index 0000000000..ed6b089489 --- /dev/null +++ b/src/net/sock_stub.go @@ -0,0 +1,15 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build nacl solaris + +package net + +import "syscall" + +func maxListenerBacklog() int { + // TODO: Implement this + // NOTE: Never return a number bigger than 1<<16 - 1. See issue 5030. + return syscall.SOMAXCONN +} diff --git a/src/net/sockopt_bsd.go b/src/net/sockopt_bsd.go index 2d36a55953..00e4dbf376 100644 --- a/src/net/sockopt_bsd.go +++ b/src/net/sockopt_bsd.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd nacl netbsd openbsd +// +build darwin dragonfly freebsd netbsd openbsd package net diff --git a/src/net/sockopt_posix.go b/src/net/sockopt_posix.go index 921918c37f..1654d1b85e 100644 --- a/src/net/sockopt_posix.go +++ b/src/net/sockopt_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows package net diff --git a/src/net/sockopt_stub.go b/src/net/sockopt_stub.go new file mode 100644 index 0000000000..de5ee0bb63 --- /dev/null +++ b/src/net/sockopt_stub.go @@ -0,0 +1,37 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build nacl + +package net + +import "syscall" + +func setDefaultSockopts(s, family, sotype int, ipv6only bool) error { + return nil +} + +func setDefaultListenerSockopts(s int) error { + return nil +} + +func setDefaultMulticastSockopts(s int) error { + return nil +} + +func setReadBuffer(fd *netFD, bytes int) error { + return syscall.ENOPROTOOPT +} + +func setWriteBuffer(fd *netFD, bytes int) error { + return syscall.ENOPROTOOPT +} + +func setKeepAlive(fd *netFD, keepalive bool) error { + return syscall.ENOPROTOOPT +} + +func setLinger(fd *netFD, sec int) error { + return syscall.ENOPROTOOPT +} diff --git a/src/net/sockoptip_bsd.go b/src/net/sockoptip_bsd.go index 87132f0f46..2199e480d4 100644 --- a/src/net/sockoptip_bsd.go +++ b/src/net/sockoptip_bsd.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd nacl netbsd openbsd +// +build darwin dragonfly freebsd netbsd openbsd package net diff --git a/src/net/sockoptip_posix.go b/src/net/sockoptip_posix.go index b5c80e4490..c2579be911 100644 --- a/src/net/sockoptip_posix.go +++ b/src/net/sockoptip_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux nacl netbsd openbsd windows +// +build darwin dragonfly freebsd linux netbsd openbsd windows package net diff --git a/src/net/sockoptip_stub.go b/src/net/sockoptip_stub.go index dcd3a22b57..32ec5ddb85 100644 --- a/src/net/sockoptip_stub.go +++ b/src/net/sockoptip_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build solaris +// +build nacl solaris package net @@ -10,30 +10,30 @@ import "syscall" func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { // See golang.org/issue/7399. - return syscall.EINVAL + return syscall.ENOPROTOOPT } func setIPv4MulticastLoopback(fd *netFD, v bool) error { // See golang.org/issue/7399. - return syscall.EINVAL + return syscall.ENOPROTOOPT } func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error { // See golang.org/issue/7399. - return syscall.EINVAL + return syscall.ENOPROTOOPT } func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error { // See golang.org/issue/7399. - return syscall.EINVAL + return syscall.ENOPROTOOPT } func setIPv6MulticastLoopback(fd *netFD, v bool) error { // See golang.org/issue/7399. - return syscall.EINVAL + return syscall.ENOPROTOOPT } func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error { // See golang.org/issue/7399. - return syscall.EINVAL + return syscall.ENOPROTOOPT } diff --git a/src/net/tcpsockopt_openbsd.go b/src/net/tcpsockopt_openbsd.go new file mode 100644 index 0000000000..041e1786a9 --- /dev/null +++ b/src/net/tcpsockopt_openbsd.go @@ -0,0 +1,16 @@ +// Copyright 2009 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 net + +import ( + "syscall" + "time" +) + +func setKeepAlivePeriod(fd *netFD, d time.Duration) error { + // OpenBSD has no user-settable per-socket TCP keepalive + // options. + return syscall.ENOPROTOOPT +} diff --git a/src/net/tcpsockopt_posix.go b/src/net/tcpsockopt_posix.go index 6484bad4b4..0abf3f97f6 100644 --- a/src/net/tcpsockopt_posix.go +++ b/src/net/tcpsockopt_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows package net diff --git a/src/net/tcpsockopt_stub.go b/src/net/tcpsockopt_stub.go index 346293ca46..b413a764d8 100644 --- a/src/net/tcpsockopt_stub.go +++ b/src/net/tcpsockopt_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build nacl openbsd +// +build nacl package net @@ -11,8 +11,10 @@ import ( "time" ) +func setNoDelay(fd *netFD, noDelay bool) error { + return syscall.ENOPROTOOPT +} + func setKeepAlivePeriod(fd *netFD, d time.Duration) error { - // NaCl and OpenBSD have no user-settable per-socket TCP - // keepalive options. return syscall.ENOPROTOOPT } diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go index 6f77ac38ae..5fd439b8bb 100644 --- a/src/os/exec/exec_test.go +++ b/src/os/exec/exec_test.go @@ -383,8 +383,9 @@ func TestExtraFilesFDShuffle(t *testing.T) { } func TestExtraFiles(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("no operating system support; skipping") + switch runtime.GOOS { + case "nacl", "windows": + t.Skipf("skipping test on %q", runtime.GOOS) } // Ensure that file descriptors have not already been leaked into -- cgit v1.3 From 73a82db1c8eec314700e2d0f92074a901fa112ed Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Thu, 18 Sep 2014 09:45:58 -0700 Subject: fmt: fix allocation tests Converting an integer to an interface{} allocates as of CL 130240043. Fixes #8617. LGTM=r R=r CC=golang-codereviews, khr https://golang.org/cl/141700043 --- src/fmt/fmt_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go index 89dde2b64a..a212c9f702 100644 --- a/src/fmt/fmt_test.go +++ b/src/fmt/fmt_test.go @@ -864,9 +864,9 @@ var mallocTest = []struct { }{ {0, `Sprintf("")`, func() { Sprintf("") }}, {1, `Sprintf("xxx")`, func() { Sprintf("xxx") }}, - {1, `Sprintf("%x")`, func() { Sprintf("%x", 7) }}, + {2, `Sprintf("%x")`, func() { Sprintf("%x", 7) }}, {2, `Sprintf("%s")`, func() { Sprintf("%s", "hello") }}, - {1, `Sprintf("%x %x")`, func() { Sprintf("%x %x", 7, 112) }}, + {3, `Sprintf("%x %x")`, func() { Sprintf("%x %x", 7, 112) }}, {2, `Sprintf("%g")`, func() { Sprintf("%g", float32(3.14159)) }}, // TODO: Can this be 1? {1, `Fprintf(buf, "%s")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%s", "hello") }}, // If the interface value doesn't need to allocate, amortized allocation overhead should be zero. -- cgit v1.3 From dfd4123edc1863a5b3b6d7cfabba6856c319ba5b Mon Sep 17 00:00:00 2001 From: Dmitriy Vyukov Date: Thu, 18 Sep 2014 10:13:15 -0700 Subject: encoding/gob: speedup encoding Replace typeLock with copy-on-write map using atomic.Value. benchmark old ns/op new ns/op delta BenchmarkEndToEndPipe 7722 7709 -0.17% BenchmarkEndToEndPipe-2 5114 4344 -15.06% BenchmarkEndToEndPipe-4 3192 2429 -23.90% BenchmarkEndToEndPipe-8 1833 1438 -21.55% BenchmarkEndToEndPipe-16 1332 983 -26.20% BenchmarkEndToEndPipe-32 1444 675 -53.25% BenchmarkEndToEndByteBuffer 6474 6019 -7.03% BenchmarkEndToEndByteBuffer-2 4280 2810 -34.35% BenchmarkEndToEndByteBuffer-4 2264 1774 -21.64% BenchmarkEndToEndByteBuffer-8 1275 979 -23.22% BenchmarkEndToEndByteBuffer-16 1257 753 -40.10% BenchmarkEndToEndByteBuffer-32 1342 644 -52.01% BenchmarkEndToEndArrayByteBuffer 727725 671349 -7.75% BenchmarkEndToEndArrayByteBuffer-2 394079 320473 -18.68% BenchmarkEndToEndArrayByteBuffer-4 211785 178175 -15.87% BenchmarkEndToEndArrayByteBuffer-8 141003 118857 -15.71% BenchmarkEndToEndArrayByteBuffer-16 139249 86367 -37.98% BenchmarkEndToEndArrayByteBuffer-32 144128 73454 -49.04% LGTM=r R=golang-codereviews, r CC=golang-codereviews https://golang.org/cl/147720043 --- src/encoding/gob/encode.go | 77 ++++++++++++++++++++++----------------------- src/encoding/gob/encoder.go | 4 --- src/encoding/gob/type.go | 76 ++++++++++++++++++++++++++++++-------------- 3 files changed, 91 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/encoding/gob/encode.go b/src/encoding/gob/encode.go index 5d35db20e6..b7bf8b0022 100644 --- a/src/encoding/gob/encode.go +++ b/src/encoding/gob/encode.go @@ -470,7 +470,7 @@ var encOpTable = [...]encOp{ // encOpFor returns (a pointer to) the encoding op for the base type under rt and // the indirection count to reach it. -func encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp) (*encOp, int) { +func encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp, building map[*typeInfo]bool) (*encOp, int) { ut := userType(rt) // If the type implements GobEncoder, we handle it without further processing. if ut.externalEnc != 0 { @@ -498,7 +498,7 @@ func encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp) (*encOp, int) break } // Slices have a header; we decode it to find the underlying array. - elemOp, elemIndir := encOpFor(t.Elem(), inProgress) + elemOp, elemIndir := encOpFor(t.Elem(), inProgress, building) op = func(i *encInstr, state *encoderState, slice reflect.Value) { if !state.sendZero && slice.Len() == 0 { return @@ -508,14 +508,14 @@ func encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp) (*encOp, int) } case reflect.Array: // True arrays have size in the type. - elemOp, elemIndir := encOpFor(t.Elem(), inProgress) + elemOp, elemIndir := encOpFor(t.Elem(), inProgress, building) op = func(i *encInstr, state *encoderState, array reflect.Value) { state.update(i) state.enc.encodeArray(state.b, array, *elemOp, elemIndir, array.Len()) } case reflect.Map: - keyOp, keyIndir := encOpFor(t.Key(), inProgress) - elemOp, elemIndir := encOpFor(t.Elem(), inProgress) + keyOp, keyIndir := encOpFor(t.Key(), inProgress, building) + elemOp, elemIndir := encOpFor(t.Elem(), inProgress, building) op = func(i *encInstr, state *encoderState, mv reflect.Value) { // We send zero-length (but non-nil) maps because the // receiver might want to use the map. (Maps don't use append.) @@ -527,12 +527,13 @@ func encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp) (*encOp, int) } case reflect.Struct: // Generate a closure that calls out to the engine for the nested type. - getEncEngine(userType(typ)) + getEncEngine(userType(typ), building) info := mustGetTypeInfo(typ) op = func(i *encInstr, state *encoderState, sv reflect.Value) { state.update(i) // indirect through info to delay evaluation for recursive structs - state.enc.encodeStruct(state.b, info.encoder, sv) + enc := info.encoder.Load().(*encEngine) + state.enc.encodeStruct(state.b, enc, sv) } case reflect.Interface: op = func(i *encInstr, state *encoderState, iv reflect.Value) { @@ -579,7 +580,7 @@ func gobEncodeOpFor(ut *userTypeInfo) (*encOp, int) { } // compileEnc returns the engine to compile the type. -func compileEnc(ut *userTypeInfo) *encEngine { +func compileEnc(ut *userTypeInfo, building map[*typeInfo]bool) *encEngine { srt := ut.base engine := new(encEngine) seen := make(map[reflect.Type]*encOp) @@ -593,7 +594,7 @@ func compileEnc(ut *userTypeInfo) *encEngine { if !isSent(&f) { continue } - op, indir := encOpFor(f.Type, seen) + op, indir := encOpFor(f.Type, seen, building) engine.instr = append(engine.instr, encInstr{*op, wireFieldNum, f.Index, indir}) wireFieldNum++ } @@ -603,49 +604,47 @@ func compileEnc(ut *userTypeInfo) *encEngine { engine.instr = append(engine.instr, encInstr{encStructTerminator, 0, nil, 0}) } else { engine.instr = make([]encInstr, 1) - op, indir := encOpFor(rt, seen) + op, indir := encOpFor(rt, seen, building) engine.instr[0] = encInstr{*op, singletonField, nil, indir} } return engine } // getEncEngine returns the engine to compile the type. -// typeLock must be held (or we're in initialization and guaranteed single-threaded). -func getEncEngine(ut *userTypeInfo) *encEngine { - info, err1 := getTypeInfo(ut) - if err1 != nil { - error_(err1) - } - if info.encoder == nil { - // Assign the encEngine now, so recursive types work correctly. But... - info.encoder = new(encEngine) - // ... if we fail to complete building the engine, don't cache the half-built machine. - // Doing this here means we won't cache a type that is itself OK but - // that contains a nested type that won't compile. The result is consistent - // error behavior when Encode is called multiple times on the top-level type. - ok := false - defer func() { - if !ok { - info.encoder = nil - } - }() - info.encoder = compileEnc(ut) - ok = true +func getEncEngine(ut *userTypeInfo, building map[*typeInfo]bool) *encEngine { + info, err := getTypeInfo(ut) + if err != nil { + error_(err) + } + enc, ok := info.encoder.Load().(*encEngine) + if !ok { + enc = buildEncEngine(info, ut, building) } - return info.encoder + return enc } -// lockAndGetEncEngine is a function that locks and compiles. -// This lets us hold the lock only while compiling, not when encoding. -func lockAndGetEncEngine(ut *userTypeInfo) *encEngine { - typeLock.Lock() - defer typeLock.Unlock() - return getEncEngine(ut) +func buildEncEngine(info *typeInfo, ut *userTypeInfo, building map[*typeInfo]bool) *encEngine { + // Check for recursive types. + if building != nil && building[info] { + return nil + } + info.encInit.Lock() + defer info.encInit.Unlock() + enc, ok := info.encoder.Load().(*encEngine) + if !ok { + if building == nil { + building = make(map[*typeInfo]bool) + } + building[info] = true + enc = compileEnc(ut, building) + info.encoder.Store(enc) + } + return enc } func (enc *Encoder) encode(b *bytes.Buffer, value reflect.Value, ut *userTypeInfo) { defer catchError(&enc.err) - engine := lockAndGetEncEngine(ut) + engine := getEncEngine(ut, nil) indir := ut.indir if ut.externalEnc != 0 { indir = int(ut.encIndir) diff --git a/src/encoding/gob/encoder.go b/src/encoding/gob/encoder.go index a3301c3bd3..4b5dc16c79 100644 --- a/src/encoding/gob/encoder.go +++ b/src/encoding/gob/encoder.go @@ -88,9 +88,7 @@ func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTyp if _, alreadySent := enc.sent[actual]; alreadySent { return false } - typeLock.Lock() info, err := getTypeInfo(ut) - typeLock.Unlock() if err != nil { enc.setError(err) return @@ -191,9 +189,7 @@ func (enc *Encoder) sendTypeDescriptor(w io.Writer, state *encoderState, ut *use // a singleton basic type (int, []byte etc.) at top level. We don't // need to send the type info but we do need to update enc.sent. if !sent { - typeLock.Lock() info, err := getTypeInfo(ut) - typeLock.Unlock() if err != nil { enc.setError(err) return diff --git a/src/encoding/gob/type.go b/src/encoding/gob/type.go index cad1452795..a49b71a867 100644 --- a/src/encoding/gob/type.go +++ b/src/encoding/gob/type.go @@ -11,6 +11,7 @@ import ( "os" "reflect" "sync" + "sync/atomic" "unicode" "unicode/utf8" ) @@ -681,29 +682,51 @@ func (w *wireType) string() string { type typeInfo struct { id typeId - encoder *encEngine + encInit sync.Mutex // protects creation of encoder + encoder atomic.Value // *encEngine wire *wireType } -var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock +// typeInfoMap is an atomic pointer to map[reflect.Type]*typeInfo. +// It's updated copy-on-write. Readers just do an atomic load +// to get the current version of the map. Writers make a full copy of +// the map and atomically update the pointer to point to the new map. +// Under heavy read contention, this is significantly faster than a map +// protected by a mutex. +var typeInfoMap atomic.Value + +func lookupTypeInfo(rt reflect.Type) *typeInfo { + m, _ := typeInfoMap.Load().(map[reflect.Type]*typeInfo) + return m[rt] +} -// typeLock must be held. func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) { rt := ut.base if ut.externalEnc != 0 { // We want the user type, not the base type. rt = ut.user } - info, ok := typeInfoMap[rt] - if ok { + if info := lookupTypeInfo(rt); info != nil { return info, nil } - info = new(typeInfo) + return buildTypeInfo(ut, rt) +} + +// buildTypeInfo constructs the type information for the type +// and stores it in the type info map. +func buildTypeInfo(ut *userTypeInfo, rt reflect.Type) (*typeInfo, error) { + typeLock.Lock() + defer typeLock.Unlock() + + if info := lookupTypeInfo(rt); info != nil { + return info, nil + } + gt, err := getBaseType(rt.Name(), rt) if err != nil { return nil, err } - info.id = gt.id() + info := &typeInfo{id: gt.id()} if ut.externalEnc != 0 { userType, err := getType(rt.Name(), ut, rt) @@ -719,25 +742,32 @@ func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) { case xText: info.wire = &wireType{TextMarshalerT: gt} } - typeInfoMap[ut.user] = info - return info, nil + rt = ut.user + } else { + t := info.id.gobType() + switch typ := rt; typ.Kind() { + case reflect.Array: + info.wire = &wireType{ArrayT: t.(*arrayType)} + case reflect.Map: + info.wire = &wireType{MapT: t.(*mapType)} + case reflect.Slice: + // []byte == []uint8 is a special case handled separately + if typ.Elem().Kind() != reflect.Uint8 { + info.wire = &wireType{SliceT: t.(*sliceType)} + } + case reflect.Struct: + info.wire = &wireType{StructT: t.(*structType)} + } } - t := info.id.gobType() - switch typ := rt; typ.Kind() { - case reflect.Array: - info.wire = &wireType{ArrayT: t.(*arrayType)} - case reflect.Map: - info.wire = &wireType{MapT: t.(*mapType)} - case reflect.Slice: - // []byte == []uint8 is a special case handled separately - if typ.Elem().Kind() != reflect.Uint8 { - info.wire = &wireType{SliceT: t.(*sliceType)} - } - case reflect.Struct: - info.wire = &wireType{StructT: t.(*structType)} + // Create new map with old contents plus new entry. + newm := make(map[reflect.Type]*typeInfo) + m, _ := typeInfoMap.Load().(map[reflect.Type]*typeInfo) + for k, v := range m { + newm[k] = v } - typeInfoMap[rt] = info + newm[rt] = info + typeInfoMap.Store(newm) return info, nil } -- cgit v1.3 From 76c7548162a42666f76359a4f5cb819624b86ae2 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 14:48:26 -0400 Subject: net: disable TestDialMultiFDLeak It fails about 25% of the time on OS X. I don't know what it's trying to do. Created issue 8764 to correct this, but for now disable. LGTM=bradfitz, mikioh.mikioh R=bradfitz, mikioh.mikioh CC=golang-codereviews https://golang.org/cl/144070044 --- src/net/dial_test.go | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/net/dial_test.go b/src/net/dial_test.go index abeb500098..42898d669f 100644 --- a/src/net/dial_test.go +++ b/src/net/dial_test.go @@ -339,6 +339,8 @@ func numTCP() (ntcp, nopen, nclose int, err error) { } func TestDialMultiFDLeak(t *testing.T) { + t.Skip("flaky test - golang.org/issue/8764") + if !supportsIPv4 || !supportsIPv6 { t.Skip("neither ipv4 nor ipv6 is supported") } -- cgit v1.3 From 98a5f52ef0658f1e1ad823bfad91dd5bbc261a75 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 14:48:47 -0400 Subject: os: avoid error result when dir is removed out from under RemoveAll Fixes #7776. LGTM=bradfitz R=golang-codereviews, bradfitz CC=golang-codereviews, r https://golang.org/cl/145860043 --- src/os/os_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ src/os/path.go | 5 ++++- 2 files changed, 46 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/os/os_test.go b/src/os/os_test.go index 0224c9b01d..7a28497414 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -18,6 +18,7 @@ import ( "runtime" "sort" "strings" + "sync" "syscall" "testing" "text/template" @@ -1403,3 +1404,44 @@ func TestNilFileMethods(t *testing.T) { } } } + +func mkdirTree(t *testing.T, root string, level, max int) { + if level >= max { + return + } + level++ + for i := 'a'; i < 'c'; i++ { + dir := filepath.Join(root, string(i)) + if err := Mkdir(dir, 0700); err != nil { + t.Fatal(err) + } + mkdirTree(t, dir, level, max) + } +} + +// Test that simultaneous RemoveAll do not report an error. +// As long as it gets removed, we should be happy. +func TestRemoveAllRace(t *testing.T) { + n := runtime.GOMAXPROCS(16) + defer runtime.GOMAXPROCS(n) + root, err := ioutil.TempDir("", "issue") + if err != nil { + t.Fatal(err) + } + mkdirTree(t, root, 1, 6) + hold := make(chan struct{}) + var wg sync.WaitGroup + for i := 0; i < 4; i++ { + wg.Add(1) + go func() { + defer wg.Done() + <-hold + err := RemoveAll(root) + if err != nil { + t.Errorf("unexpected error: %T, %q", err, err) + } + }() + } + close(hold) // let workers race to remove root + wg.Wait() +} diff --git a/src/os/path.go b/src/os/path.go index 02a77ec805..6cc69403b6 100644 --- a/src/os/path.go +++ b/src/os/path.go @@ -66,7 +66,7 @@ func MkdirAll(path string, perm FileMode) error { func RemoveAll(path string) error { // Simple case: if Remove works, we're done. err := Remove(path) - if err == nil { + if err == nil || IsNotExist(err) { return nil } @@ -116,6 +116,9 @@ func RemoveAll(path string) error { // Remove directory. err1 := Remove(path) + if err1 == nil || IsNotExist(err1) { + return nil + } if err == nil { err = err1 } -- cgit v1.3 From c3b5db895b11ba28bc1546f37178efcb057ab3f0 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 14:49:24 -0400 Subject: runtime: delete panicstring; move its checks into gopanic In Go 1.3 the runtime called panicstring to report errors like divide by zero or memory faults. Now we call panic (gopanic) with pre-allocated error values. That new path is missing the checking that panicstring did, so add it there. The only call to panicstring left is in cnew, which is problematic because if it fails, probably the heap is corrupt. In that case, calling panicstring creates a new errorCString (no allocation there), but then panic tries to print it, invoking errorCString.Error, which does a string concatenation (allocating), which then dies. Replace that one panicstring with a throw: cnew is for allocating runtime data structures and should never ask for an inappropriate amount of memory. With panicstring gone, delete newErrorCString, errorCString. While we're here, delete newErrorString, not called by anyone. (It can't be: that would be C code calling Go code that might block or grow the stack.) Found while debugging a malloc corruption. This resulted in 'panic during panic' instead of a more useful message. LGTM=khr R=khr CC=golang-codereviews https://golang.org/cl/138290045 --- src/runtime/error.go | 22 ------------------- src/runtime/malloc.c | 2 +- src/runtime/panic.go | 59 +++++++++++++++++++++++++-------------------------- src/runtime/proc.c | 7 ------ src/runtime/runtime.h | 3 --- 5 files changed, 30 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/runtime/error.go b/src/runtime/error.go index 3ea93680ce..0b40c702b0 100644 --- a/src/runtime/error.go +++ b/src/runtime/error.go @@ -71,28 +71,6 @@ func (e errorString) Error() string { return "runtime error: " + string(e) } -// For calling from C. -func newErrorString(s string, ret *interface{}) { - *ret = errorString(s) -} - -// An errorCString represents a runtime error described by a single C string. -// Not "type errorCString unsafe.Pointer" because of http://golang.org/issue/7084. -// Not uintptr because we want to avoid an allocation if interfaces can't hold -// uintptrs directly (and cstr _is_ a pointer). -type errorCString struct{ cstr unsafe.Pointer } - -func (e errorCString) RuntimeError() {} - -func (e errorCString) Error() string { - return "runtime error: " + gostringnocopy((*byte)(e.cstr)) -} - -// For calling from C. -func newErrorCString(s unsafe.Pointer, ret *interface{}) { - *ret = errorCString{s} -} - type stringer interface { String() string } diff --git a/src/runtime/malloc.c b/src/runtime/malloc.c index d5f2b9ab80..60d20a992d 100644 --- a/src/runtime/malloc.c +++ b/src/runtime/malloc.c @@ -335,7 +335,7 @@ static void* cnew(Type *typ, intgo n) { if(n < 0 || (typ->size > 0 && n > MaxMem/typ->size)) - runtime·panicstring("runtime: allocation size out of range"); + runtime·throw("runtime: allocation size out of range"); return runtime·mallocgc(typ->size*n, typ, typ->kind&KindNoPointers ? FlagNoScan : 0); } diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 927b6db44b..3cc31053e8 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -281,6 +281,35 @@ func gopanic(e interface{}) { if gp.m.curg != gp { gothrow("panic on m stack") } + + // m.softfloat is set during software floating point. + // It increments m.locks to avoid preemption. + // We moved the memory loads out, so there shouldn't be + // any reason for it to panic anymore. + if gp.m.softfloat != 0 { + gp.m.locks-- + gp.m.softfloat = 0 + gothrow("panic during softfloat") + } + if gp.m.mallocing != 0 { + print("panic: ") + printany(e) + print("\n") + gothrow("panic during malloc") + } + if gp.m.gcing != 0 { + print("panic: ") + printany(e) + print("\n") + gothrow("panic during gc") + } + if gp.m.locks != 0 { + print("panic: ") + printany(e) + print("\n") + gothrow("panic holding locks") + } + var p _panic p.arg = e p.link = gp._panic @@ -431,33 +460,3 @@ func gothrow(s string) { dopanic(0) *(*int)(nil) = 0 // not reached } - -func panicstring(s *int8) { - // m.softfloat is set during software floating point, - // which might cause a fault during a memory load. - // It increments m.locks to avoid preemption. - // If we're panicking, the software floating point frames - // will be unwound, so decrement m.locks as they would. - gp := getg() - if gp.m.softfloat != 0 { - gp.m.locks-- - gp.m.softfloat = 0 - } - - if gp.m.mallocing != 0 { - print("panic: ", s, "\n") - gothrow("panic during malloc") - } - if gp.m.gcing != 0 { - print("panic: ", s, "\n") - gothrow("panic during gc") - } - if gp.m.locks != 0 { - print("panic: ", s, "\n") - gothrow("panic holding locks") - } - - var err interface{} - newErrorCString(unsafe.Pointer(s), &err) - gopanic(err) -} diff --git a/src/runtime/proc.c b/src/runtime/proc.c index 4282a145e1..860701ee58 100644 --- a/src/runtime/proc.c +++ b/src/runtime/proc.c @@ -123,7 +123,6 @@ runtime·schedinit(void) { int32 n, procs; byte *p; - Eface i; // raceinit must be the first call to race detector. // In particular, it must be done before mallocinit below calls racemapshadow. @@ -137,12 +136,6 @@ runtime·schedinit(void) runtime·mallocinit(); mcommoninit(g->m); - // Initialize the itable value for newErrorCString, - // so that the next time it gets called, possibly - // in a fault during a garbage collection, it will not - // need to allocated memory. - runtime·newErrorCString(0, &i); - runtime·goargs(); runtime·goenvs(); runtime·parsedebugvars(); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index c034f3aa97..386b09b96b 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -804,7 +804,6 @@ void runtime·goenvs(void); void runtime·goenvs_unix(void); void* runtime·getu(void); void runtime·throw(int8*); -void runtime·panicstring(int8*); bool runtime·canpanic(G*); void runtime·prints(int8*); void runtime·printf(int8*, ...); @@ -1063,8 +1062,6 @@ void runtime·panicdivide(void); */ void runtime·printany(Eface); void runtime·newTypeAssertionError(String*, String*, String*, String*, Eface*); -void runtime·newErrorString(String, Eface*); -void runtime·newErrorCString(int8*, Eface*); void runtime·fadd64c(uint64, uint64, uint64*); void runtime·fsub64c(uint64, uint64, uint64*); void runtime·fmul64c(uint64, uint64, uint64*); -- cgit v1.3 From 45143aeca47da4595367e9ab0f1d0194f7847a96 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Thu, 18 Sep 2014 23:25:11 +0200 Subject: runtime: fix handling of GOTRACEBACK Since CL 130990043, the GOTRACEBACK variable is only used when the GODEBUG variable is set. This change restores the original behavior. LGTM=rsc R=golang-codereviews, aram, gobot, r, rsc CC=golang-codereviews https://golang.org/cl/132520043 --- src/runtime/runtime.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index ae754dc5cd..aa8dd8f7a0 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -287,18 +287,18 @@ runtime·parsedebugvars(void) intgo i, n; p = runtime·getenv("GODEBUG"); - if(p == nil) - return; - for(;;) { - for(i=0; i Date: Thu, 18 Sep 2014 15:43:06 -0700 Subject: go/doc: treat _ consts as exported Fixes #5397. LGTM=adg R=gri, adg CC=golang-codereviews, rsc https://golang.org/cl/144110044 --- src/go/doc/exports.go | 21 +++++++++-------- src/go/doc/testdata/blank.0.golden | 37 ++++++++++++++++++++++++++++++ src/go/doc/testdata/blank.1.golden | 46 ++++++++++++++++++++++++++++++++++++++ src/go/doc/testdata/blank.2.golden | 37 ++++++++++++++++++++++++++++++ src/go/doc/testdata/blank.go | 38 +++++++++++++++++++++++++++++++ 5 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 src/go/doc/testdata/blank.0.golden create mode 100644 src/go/doc/testdata/blank.1.golden create mode 100644 src/go/doc/testdata/blank.2.golden create mode 100644 src/go/doc/testdata/blank.go (limited to 'src') diff --git a/src/go/doc/exports.go b/src/go/doc/exports.go index ff01285d4c..9b421e7341 100644 --- a/src/go/doc/exports.go +++ b/src/go/doc/exports.go @@ -6,15 +6,18 @@ package doc -import "go/ast" +import ( + "go/ast" + "go/token" +) // filterIdentList removes unexported names from list in place // and returns the resulting list. // -func filterIdentList(list []*ast.Ident) []*ast.Ident { +func filterIdentList(list []*ast.Ident, blankOk bool) []*ast.Ident { j := 0 for _, x := range list { - if ast.IsExported(x.Name) { + if ast.IsExported(x.Name) || (blankOk && x.Name == "_") { list[j] = x j++ } @@ -74,7 +77,7 @@ func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp r.remember(ityp) } } else { - field.Names = filterIdentList(field.Names) + field.Names = filterIdentList(field.Names, false) if len(field.Names) < n { removedFields = true } @@ -136,13 +139,13 @@ func (r *reader) filterType(parent *namedType, typ ast.Expr) { } } -func (r *reader) filterSpec(spec ast.Spec) bool { +func (r *reader) filterSpec(spec ast.Spec, tok token.Token) bool { switch s := spec.(type) { case *ast.ImportSpec: // always keep imports so we can collect them return true case *ast.ValueSpec: - s.Names = filterIdentList(s.Names) + s.Names = filterIdentList(s.Names, tok == token.CONST) if len(s.Names) > 0 { r.filterType(nil, s.Type) return true @@ -159,10 +162,10 @@ func (r *reader) filterSpec(spec ast.Spec) bool { return false } -func (r *reader) filterSpecList(list []ast.Spec) []ast.Spec { +func (r *reader) filterSpecList(list []ast.Spec, tok token.Token) []ast.Spec { j := 0 for _, s := range list { - if r.filterSpec(s) { + if r.filterSpec(s, tok) { list[j] = s j++ } @@ -173,7 +176,7 @@ func (r *reader) filterSpecList(list []ast.Spec) []ast.Spec { func (r *reader) filterDecl(decl ast.Decl) bool { switch d := decl.(type) { case *ast.GenDecl: - d.Specs = r.filterSpecList(d.Specs) + d.Specs = r.filterSpecList(d.Specs, d.Tok) return len(d.Specs) > 0 case *ast.FuncDecl: // ok to filter these methods early because any diff --git a/src/go/doc/testdata/blank.0.golden b/src/go/doc/testdata/blank.0.golden new file mode 100644 index 0000000000..dae3ab2aff --- /dev/null +++ b/src/go/doc/testdata/blank.0.golden @@ -0,0 +1,37 @@ +// Package blank is a go/doc test for the handling of _. See issue ... +PACKAGE blank + +IMPORTPATH + testdata/blank + +FILENAMES + testdata/blank.go + +CONSTANTS + // Package constants. + const ( + _ int = iota + I1 + I2 + ) + + +TYPES + // S has a padding field. + type S struct { + H uint32 + + A uint8 + // contains filtered or unexported fields + } + + // + type T int + + // T constants. + const ( + _ T = iota + T1 + T2 + ) + diff --git a/src/go/doc/testdata/blank.1.golden b/src/go/doc/testdata/blank.1.golden new file mode 100644 index 0000000000..333d7e5b04 --- /dev/null +++ b/src/go/doc/testdata/blank.1.golden @@ -0,0 +1,46 @@ +// Package blank is a go/doc test for the handling of _. See issue ... +PACKAGE blank + +IMPORTPATH + testdata/blank + +FILENAMES + testdata/blank.go + +CONSTANTS + // Package constants. + const ( + _ int = iota + I1 + I2 + ) + + +VARIABLES + // + var _ = T(55) + + +FUNCTIONS + // + func _() + + +TYPES + // S has a padding field. + type S struct { + H uint32 + _ uint8 + A uint8 + } + + // + type T int + + // T constants. + const ( + _ T = iota + T1 + T2 + ) + diff --git a/src/go/doc/testdata/blank.2.golden b/src/go/doc/testdata/blank.2.golden new file mode 100644 index 0000000000..dae3ab2aff --- /dev/null +++ b/src/go/doc/testdata/blank.2.golden @@ -0,0 +1,37 @@ +// Package blank is a go/doc test for the handling of _. See issue ... +PACKAGE blank + +IMPORTPATH + testdata/blank + +FILENAMES + testdata/blank.go + +CONSTANTS + // Package constants. + const ( + _ int = iota + I1 + I2 + ) + + +TYPES + // S has a padding field. + type S struct { + H uint32 + + A uint8 + // contains filtered or unexported fields + } + + // + type T int + + // T constants. + const ( + _ T = iota + T1 + T2 + ) + diff --git a/src/go/doc/testdata/blank.go b/src/go/doc/testdata/blank.go new file mode 100644 index 0000000000..f812c77b77 --- /dev/null +++ b/src/go/doc/testdata/blank.go @@ -0,0 +1,38 @@ +// Copyright 2014 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 blank is a go/doc test for the handling of _. +// See issue 5397. +package blank + +type T int + +// T constants. +const ( + _ T = iota + T1 + T2 +) + +// Package constants. +const ( + _ int = iota + I1 + I2 +) + +// Blanks not in doc output: + +// S has a padding field. +type S struct { + H uint32 + _ uint8 + A uint8 +} + +func _() {} + +type _ T + +var _ = T(55) -- cgit v1.3 From ab76638cdc1705ec4f22cb6f757c1b542b388692 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 19:40:06 -0400 Subject: syscall: fix infinite recursion in itoa Fixes #8332. LGTM=dvyukov R=golang-codereviews, dvyukov CC=golang-codereviews https://golang.org/cl/138650044 --- src/syscall/export_test.go | 7 +++++++ src/syscall/str.go | 6 +++++- src/syscall/syscall_test.go | 17 +++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 src/syscall/export_test.go (limited to 'src') diff --git a/src/syscall/export_test.go b/src/syscall/export_test.go new file mode 100644 index 0000000000..c9774622c8 --- /dev/null +++ b/src/syscall/export_test.go @@ -0,0 +1,7 @@ +// Copyright 2014 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 syscall + +var Itoa = itoa diff --git a/src/syscall/str.go b/src/syscall/str.go index 0fce842e8c..2ddf04b227 100644 --- a/src/syscall/str.go +++ b/src/syscall/str.go @@ -6,8 +6,12 @@ package syscall func itoa(val int) string { // do it here rather than with fmt to avoid dependency if val < 0 { - return "-" + itoa(-val) + return "-" + uitoa(uint(-val)) } + return uitoa(uint(val)) +} + +func uitoa(val uint) string { var buf [32]byte // big enough for int64 i := len(buf) - 1 for val >= 10 { diff --git a/src/syscall/syscall_test.go b/src/syscall/syscall_test.go index 2a39b54f1b..846c4873d2 100644 --- a/src/syscall/syscall_test.go +++ b/src/syscall/syscall_test.go @@ -5,6 +5,7 @@ package syscall_test import ( + "fmt" "syscall" "testing" ) @@ -28,3 +29,19 @@ func TestEnv(t *testing.T) { // make sure TESTENV gets set to "", not deleted testSetGetenv(t, "TESTENV", "") } + +func TestItoa(t *testing.T) { + // Make most negative integer: 0x8000... + i := 1 + for i<<1 != 0 { + i <<= 1 + } + if i >= 0 { + t.Fatal("bad math") + } + s := syscall.Itoa(i) + f := fmt.Sprint(i) + if s != f { + t.Fatalf("itoa(%d) = %s, want %s", i, s, f) + } +} -- cgit v1.3 From e9ec8e7a2693b322287a96100750c6f00cf574ef Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 19:40:31 -0400 Subject: bytes, strings: document that FieldsFunc f must not be stateful Fixes #8738. LGTM=adg R=golang-codereviews, adg CC=golang-codereviews https://golang.org/cl/143260045 --- src/bytes/bytes.go | 2 ++ src/strings/strings.go | 2 ++ 2 files changed, 4 insertions(+) (limited to 'src') diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go index 34c22bbfb1..7634707b3c 100644 --- a/src/bytes/bytes.go +++ b/src/bytes/bytes.go @@ -267,6 +267,8 @@ func Fields(s []byte) [][]byte { // It splits the slice s at each run of code points c satisfying f(c) and // returns a slice of subslices of s. If all code points in s satisfy f(c), or // len(s) == 0, an empty slice is returned. +// FieldsFunc makes no guarantees about the order in which it calls f(c). +// If f does not return consistent results for a given c, FieldsFunc may crash. func FieldsFunc(s []byte, f func(rune) bool) [][]byte { n := 0 inField := false diff --git a/src/strings/strings.go b/src/strings/strings.go index 761f32a068..1b9df2e757 100644 --- a/src/strings/strings.go +++ b/src/strings/strings.go @@ -347,6 +347,8 @@ func Fields(s string) []string { // FieldsFunc splits the string s at each run of Unicode code points c satisfying f(c) // and returns an array of slices of s. If all code points in s satisfy f(c) or the // string is empty, an empty slice is returned. +// FieldsFunc makes no guarantees about the order in which it calls f(c). +// If f does not return consistent results for a given c, FieldsFunc may crash. func FieldsFunc(s string, f func(rune) bool) []string { // First count the fields. n := 0 -- cgit v1.3 From e4fa1e40354dfd1e16c2b1f912bfbbdc8c0dfa9f Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 18 Sep 2014 16:53:35 -0700 Subject: go/doc: document rationale for recent change LGTM=adg R=adg CC=golang-codereviews https://golang.org/cl/143290043 --- src/go/doc/exports.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/go/doc/exports.go b/src/go/doc/exports.go index 9b421e7341..1d3b466d8c 100644 --- a/src/go/doc/exports.go +++ b/src/go/doc/exports.go @@ -12,7 +12,8 @@ import ( ) // filterIdentList removes unexported names from list in place -// and returns the resulting list. +// and returns the resulting list. If blankOk is set, blank +// identifiers are considered exported names. // func filterIdentList(list []*ast.Ident, blankOk bool) []*ast.Ident { j := 0 @@ -145,6 +146,8 @@ func (r *reader) filterSpec(spec ast.Spec, tok token.Token) bool { // always keep imports so we can collect them return true case *ast.ValueSpec: + // special case: consider blank constants as exported + // (work-around for issue 5397) s.Names = filterIdentList(s.Names, tok == token.CONST) if len(s.Names) > 0 { r.filterType(nil, s.Type) -- cgit v1.3 From 84f9c42bbbfb67c2ef78d4a4e19417329d7f8f5a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 20:13:07 -0400 Subject: os: skip TestRemoveAllRace on Windows It's just fundamentally incompatible with Windows' pickiness about removing things that are in use. TBR=brainman CC=golang-codereviews https://golang.org/cl/142270043 --- src/os/os_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/os/os_test.go b/src/os/os_test.go index 7a28497414..973cc3a7bf 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -1422,6 +1422,14 @@ func mkdirTree(t *testing.T, root string, level, max int) { // Test that simultaneous RemoveAll do not report an error. // As long as it gets removed, we should be happy. func TestRemoveAllRace(t *testing.T) { + if runtime.GOOS == "windows" { + // Windows has very strict rules about things like + // removing directories while someone else has + // them open. The racing doesn't work out nicely + // like it does on Unix. + t.Skip("skipping on windows") + } + n := runtime.GOMAXPROCS(16) defer runtime.GOMAXPROCS(n) root, err := ioutil.TempDir("", "issue") -- cgit v1.3 From 5a40b568d023922e735ddc21f3b4a30d52197b7a Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 18 Sep 2014 17:27:26 -0700 Subject: lib9, cmd/ld: fixes for cross-linking on a Windows host This fixes a couple of problems that occur when the linker removes its temporary directory on Windows. The linker only creates and removes a temporary directory when doing external linking. Windows does not yet support external linking. Therefore, these problems are only seen when using a cross-compiler hosted on Windows. In lib9, FindFirstFileW returns just the file name, not the full path name. Don't assume that we will find a slash. Changed the code to work either way just in case. In ld, Windows requires that files be closed before they are removed, so close the output file before we might try to remove it. Fixes #8723. LGTM=alex.brainman R=golang-codereviews, alex.brainman CC=golang-codereviews https://golang.org/cl/141690043 --- src/cmd/ld/lib.c | 4 ++++ src/lib9/tempdir_windows.c | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 651705a2e6..36f0f99de2 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -144,6 +144,10 @@ libinit(void) void errorexit(void) { + if(cout >= 0) { + // For rmtemp run at atexit time on Windows. + close(cout); + } if(nerrors) { if(cout >= 0) mayberemoveoutfile(); diff --git a/src/lib9/tempdir_windows.c b/src/lib9/tempdir_windows.c index 1a530059ae..4c3df7cf11 100644 --- a/src/lib9/tempdir_windows.c +++ b/src/lib9/tempdir_windows.c @@ -70,7 +70,7 @@ removeall(char *p) { WinRune *r, *r1; DWORD attr; - char *q, *elem; + char *q, *qt, *elem; HANDLE h; WIN32_FIND_DATAW data; @@ -91,15 +91,18 @@ removeall(char *p) do{ q = toutf(data.cFileName); elem = strrchr(q, '\\'); - if(elem != nil) { + if(elem != nil) elem++; - if(strcmp(elem, ".") == 0 || strcmp(elem, "..") == 0) { - free(q); - continue; - } + else + elem = q; + if(strcmp(elem, ".") == 0 || strcmp(elem, "..") == 0) { + free(q); + continue; } - removeall(q); - free(q); + qt = smprint("%s\\%s", p, q); + free(q); + removeall(qt); + free(qt); }while(FindNextFileW(h, &data)); FindClose(h); -- cgit v1.3 From 54245cba1f5f3971c82be6d2b3e658f968f08e7b Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 20:35:36 -0400 Subject: runtime: show frames for exported runtime functions The current Windows build failure happens because by default runtime frames are excluded from stack traces. Apparently the Windows breakpoint path dies with an ordinary panic, while the Unix path dies with a throw. Breakpoint is a strange function and I don't mind that it's a little different on the two operating systems. The panic squelches runtime frames but the throw shows them, because throw is considered something that shouldn't have happened at all, so as much detail as possible is wanted. The runtime exclusion is meant to prevents printing too much noise about internal runtime details. But exported functions are not internal details, so show exported functions. If the program dies because you called runtime.Breakpoint, it's okay to see that frame. This makes the Breakpoint test show Breakpoint in the stack trace no matter how it is handled. Should fix Windows build. Tested on Unix by changing Breakpoint to fault instead of doing a breakpoint. TBR=brainman CC=golang-codereviews https://golang.org/cl/143300043 --- src/runtime/traceback.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 9e95fa33d5..a93c42186b 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -499,7 +499,14 @@ func showframe(f *_func, gp *g) bool { return true } - return traceback > 1 || f != nil && contains(name, ".") && !hasprefix(name, "runtime.") + return traceback > 1 || f != nil && contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name)) +} + +// isExportedRuntime reports whether name is an exported runtime function. +// It is only for runtime functions, so ASCII A-Z is fine. +func isExportedRuntime(name string) bool { + const n = len("runtime.") + return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z' } var gStatusStrings = [...]string{ -- cgit v1.3 From 048692e5de12c89b80761a9532c86eff526af640 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 20:41:00 -0400 Subject: runtime: fix Windows SysUsed Same fix as for SysUnused. Fixes #8038. LGTM=iant, alex.brainman R=golang-codereviews, iant, alex.brainman CC=golang-codereviews https://golang.org/cl/147820043 --- src/runtime/mem_windows.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src') diff --git a/src/runtime/mem_windows.c b/src/runtime/mem_windows.c index 7bc028bf3a..6ea992020c 100644 --- a/src/runtime/mem_windows.c +++ b/src/runtime/mem_windows.c @@ -68,10 +68,22 @@ void runtime·SysUsed(void *v, uintptr n) { void *r; + uintptr small; r = runtime·stdcall4(runtime·VirtualAlloc, (uintptr)v, n, MEM_COMMIT, PAGE_READWRITE); if(r != v) runtime·throw("runtime: failed to commit pages"); + + // Commit failed. See SysUnused. + while(n > 0) { + small = n; + while(small >= 4096 && runtime·stdcall4(runtime·VirtualAlloc, (uintptr)v, small, MEM_COMMIT, PAGE_READWRITE) == nil) + small = (small / 2) & ~(4096-1); + if(small < 4096) + runtime·throw("runtime: failed to decommit pages"); + v = (byte*)v + small; + n -= small; + } } void -- cgit v1.3 From f8474fa01d6995b9e442abff3f7b01a9119a22cc Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Fri, 19 Sep 2014 11:14:51 +1000 Subject: runtime: remove useless code around of EXCEPTION_BREAKPOINT This is to simplify VEH handler before making changes to fix issue 8006. Update #8006 LGTM=adg, rsc R=golang-codereviews, adg, rsc CC=golang-codereviews https://golang.org/cl/138630043 --- src/runtime/defs_windows.go | 1 - src/runtime/defs_windows_386.h | 1 - src/runtime/defs_windows_amd64.h | 1 - src/runtime/os_windows_386.c | 13 ------------- src/runtime/os_windows_amd64.c | 10 ---------- 5 files changed, 26 deletions(-) (limited to 'src') diff --git a/src/runtime/defs_windows.go b/src/runtime/defs_windows.go index 01aea92dee..cb0f54d8ab 100644 --- a/src/runtime/defs_windows.go +++ b/src/runtime/defs_windows.go @@ -49,7 +49,6 @@ const ( CONTEXT_FULL = C.CONTEXT_FULL EXCEPTION_ACCESS_VIOLATION = C.STATUS_ACCESS_VIOLATION - EXCEPTION_BREAKPOINT = C.STATUS_BREAKPOINT EXCEPTION_FLT_DENORMAL_OPERAND = C.STATUS_FLOAT_DENORMAL_OPERAND EXCEPTION_FLT_DIVIDE_BY_ZERO = C.STATUS_FLOAT_DIVIDE_BY_ZERO EXCEPTION_FLT_INEXACT_RESULT = C.STATUS_FLOAT_INEXACT_RESULT diff --git a/src/runtime/defs_windows_386.h b/src/runtime/defs_windows_386.h index db3629a1d2..295e422c6b 100644 --- a/src/runtime/defs_windows_386.h +++ b/src/runtime/defs_windows_386.h @@ -22,7 +22,6 @@ enum { CONTEXT_FULL = 0x10007, EXCEPTION_ACCESS_VIOLATION = 0xc0000005, - EXCEPTION_BREAKPOINT = 0x80000003, EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d, EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e, EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f, diff --git a/src/runtime/defs_windows_amd64.h b/src/runtime/defs_windows_amd64.h index fe26f5a84a..2516c84128 100644 --- a/src/runtime/defs_windows_amd64.h +++ b/src/runtime/defs_windows_amd64.h @@ -22,7 +22,6 @@ enum { CONTEXT_FULL = 0x10000b, EXCEPTION_ACCESS_VIOLATION = 0xc0000005, - EXCEPTION_BREAKPOINT = 0x80000003, EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d, EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e, EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f, diff --git a/src/runtime/os_windows_386.c b/src/runtime/os_windows_386.c index 15a5ea5d1f..028b09bbc8 100644 --- a/src/runtime/os_windows_386.c +++ b/src/runtime/os_windows_386.c @@ -54,19 +54,6 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp) if(r->Eip < (uint32)runtime·text || (uint32)runtime·etext < r->Eip) return 0; - switch(info->ExceptionCode) { - case EXCEPTION_BREAKPOINT: - // It is unclear whether this is needed, unclear whether it - // would work, and unclear how to test it. Leave out for now. - // This only handles breakpoint instructions written in the - // assembly sources, not breakpoints set by a debugger, and - // there are very few of the former. - // - // r->Eip--; // because 8l generates 2 bytes for INT3 - // return 0; - break; - } - if(gp != nil && runtime·issigpanic(info->ExceptionCode)) { // Make it look like a call to the signal func. // Have to pass arguments out of band since diff --git a/src/runtime/os_windows_amd64.c b/src/runtime/os_windows_amd64.c index 9a69d73c07..d7b45c5b1d 100644 --- a/src/runtime/os_windows_amd64.c +++ b/src/runtime/os_windows_amd64.c @@ -62,16 +62,6 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp) if(r->Rip < (uint64)runtime·text || (uint64)runtime·etext < r->Rip) return 0; - switch(info->ExceptionCode) { - case EXCEPTION_BREAKPOINT: - // It is unclear whether this is needed, unclear whether it - // would work, and unclear how to test it. Leave out for now. - // This only handles breakpoint instructions written in the - // assembly sources, not breakpoints set by a debugger, and - // there are very few of the former. - break; - } - if(gp != nil && runtime·issigpanic(info->ExceptionCode)) { // Make it look like a call to the signal func. // Have to pass arguments out of band since -- cgit v1.3 From 2debfeb93666dcde759b7cc3a3e001a82307a84c Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Thu, 18 Sep 2014 21:16:01 -0400 Subject: os: handle 'has been removed' error as IsNotExist on Plan 9 It fixes the TestRemoveAllRace test introduced in CL 145860043. LGTM=bradfitz R=rsc, bradfitz CC=golang-codereviews https://golang.org/cl/147820044 --- src/os/error_plan9.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/os/error_plan9.go b/src/os/error_plan9.go index 62d4e420e4..45cd747923 100644 --- a/src/os/error_plan9.go +++ b/src/os/error_plan9.go @@ -25,7 +25,7 @@ func isNotExist(err error) bool { case *LinkError: err = pe.Err } - return contains(err.Error(), "does not exist") || contains(err.Error(), "not found") + return contains(err.Error(), "does not exist") || contains(err.Error(), "not found") || contains(err.Error(), "has been removed") } func isPermission(err error) bool { -- cgit v1.3 From dd8f29e3feb91f3c7ec0681ef23694578e4d9228 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 21:19:18 -0400 Subject: reflect: adjust Value.String to give correct answer for methods Fixes #7859. LGTM=r R=adonovan, r CC=golang-codereviews https://golang.org/cl/136710043 --- src/reflect/all_test.go | 16 ++++++++++++++++ src/reflect/value.go | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 4be0e353df..b72c4b176d 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -3923,3 +3923,19 @@ func useStack(n int) { var b [1024]byte // makes frame about 1KB useStack(n - 1 + int(b[99])) } + +type Impl struct{} + +func (Impl) f() {} + +func TestValueString(t *testing.T) { + rv := ValueOf(Impl{}) + if rv.String() != "" { + t.Errorf("ValueOf(Impl{}).String() = %q, want %q", rv.String(), "") + } + + method := rv.Method(0) + if method.String() != "" { + t.Errorf("ValueOf(Impl{}).Method(0).String() = %q, want %q", method.String(), "") + } +} diff --git a/src/reflect/value.go b/src/reflect/value.go index b0dfe840b6..12d423f3c3 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1771,7 +1771,7 @@ func (v Value) String() string { } // If you call String on a reflect.Value of other type, it's better to // print something than to panic. Useful in debugging. - return "<" + v.typ.String() + " Value>" + return "<" + v.Type().String() + " Value>" } // TryRecv attempts to receive a value from the channel v but will not block. -- cgit v1.3 From 2ed209eaf59c3b7372258419c5fa1f5b0abc507e Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Fri, 19 Sep 2014 11:38:48 +1000 Subject: runtime: allow OutputDebugString to be sent to debugger We mark DBG_PRINTEXCEPTION_C messages in VEH handler as handled, thus preventing debugger from seeing them. I don't see reason for doing that. The comment warns of crashes, but I added test and don't see any crashes. This is also simplify VEH handler before making changes to fix issue 8006. Update #8006 LGTM=rsc R=golang-codereviews, rsc CC=golang-codereviews https://golang.org/cl/146800043 --- src/runtime/os_windows_386.c | 15 --------------- src/runtime/os_windows_amd64.c | 15 --------------- src/runtime/syscall_windows_test.go | 6 ++++++ 3 files changed, 6 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/runtime/os_windows_386.c b/src/runtime/os_windows_386.c index 028b09bbc8..e2ae8db277 100644 --- a/src/runtime/os_windows_386.c +++ b/src/runtime/os_windows_386.c @@ -24,8 +24,6 @@ runtime·dumpregs(Context *r) runtime·printf("gs %x\n", r->SegGs); } -#define DBG_PRINTEXCEPTION_C 0x40010006 - // Called by sigtramp from Windows VEH handler. // Return value signals whether the exception has been handled (-1) // or should be made available to other handlers in the chain (0). @@ -36,19 +34,6 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp) uintptr *sp; extern byte runtime·text[], runtime·etext[]; - if(info->ExceptionCode == DBG_PRINTEXCEPTION_C) { - // This exception is intended to be caught by debuggers. - // There is a not-very-informational message like - // "Invalid parameter passed to C runtime function" - // sitting at info->ExceptionInformation[0] (a wchar_t*), - // with length info->ExceptionInformation[1]. - // The default behavior is to ignore this exception, - // but somehow returning 0 here (meaning keep going) - // makes the program crash instead. Maybe Windows has no - // other handler registered? In any event, ignore it. - return -1; - } - // Only handle exception if executing instructions in Go binary // (not Windows library code). if(r->Eip < (uint32)runtime·text || (uint32)runtime·etext < r->Eip) diff --git a/src/runtime/os_windows_amd64.c b/src/runtime/os_windows_amd64.c index d7b45c5b1d..261880d450 100644 --- a/src/runtime/os_windows_amd64.c +++ b/src/runtime/os_windows_amd64.c @@ -32,8 +32,6 @@ runtime·dumpregs(Context *r) runtime·printf("gs %X\n", (uint64)r->SegGs); } -#define DBG_PRINTEXCEPTION_C 0x40010006 - // Called by sigtramp from Windows VEH handler. // Return value signals whether the exception has been handled (-1) // or should be made available to other handlers in the chain (0). @@ -44,19 +42,6 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp) uintptr *sp; extern byte runtime·text[], runtime·etext[]; - if(info->ExceptionCode == DBG_PRINTEXCEPTION_C) { - // This exception is intended to be caught by debuggers. - // There is a not-very-informational message like - // "Invalid parameter passed to C runtime function" - // sitting at info->ExceptionInformation[0] (a wchar_t*), - // with length info->ExceptionInformation[1]. - // The default behavior is to ignore this exception, - // but somehow returning 0 here (meaning keep going) - // makes the program crash instead. Maybe Windows has no - // other handler registered? In any event, ignore it. - return -1; - } - // Only handle exception if executing instructions in Go binary // (not Windows library code). if(r->Rip < (uint64)runtime·text || (uint64)runtime·etext < r->Rip) diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go index a828512188..9ed016ccc8 100644 --- a/src/runtime/syscall_windows_test.go +++ b/src/runtime/syscall_windows_test.go @@ -488,3 +488,9 @@ func TestRegisterClass(t *testing.T) { t.Fatalf("UnregisterClass failed: %v", err) } } + +func TestOutputDebugString(t *testing.T) { + d := GetDLL(t, "kernel32.dll") + p := syscall.StringToUTF16Ptr("testing OutputDebugString") + d.Proc("OutputDebugStringW").Call(uintptr(unsafe.Pointer(p))) +} -- cgit v1.3 From 5fdea3430aed2224a88efa764034ef2ee2b4ccb1 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 21:43:09 -0400 Subject: runtime: revise TestSetPanicOnFault We can't assume all those addresses are unmapped. But at least one should be. What we're really testing is that the program doesn't crash. Fixes #8542. LGTM=iant R=golang-codereviews, iant, minux CC=golang-codereviews https://golang.org/cl/144120043 --- src/runtime/runtime_test.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/runtime/runtime_test.go b/src/runtime/runtime_test.go index cffc9f7d35..3c4075842b 100644 --- a/src/runtime/runtime_test.go +++ b/src/runtime/runtime_test.go @@ -157,8 +157,8 @@ var faultAddrs = []uint64{ // or else malformed. 0xffffffffffffffff, 0xfffffffffffff001, - // no 0xffffffffffff0001; 0xffff0001 is mapped for 32-bit user space on OS X - // no 0xfffffffffff00001; 0xfff00001 is mapped for 32-bit user space sometimes on Linux + 0xffffffffffff0001, + 0xfffffffffff00001, 0xffffffffff000001, 0xfffffffff0000001, 0xffffffff00000001, @@ -182,26 +182,33 @@ func TestSetPanicOnFault(t *testing.T) { old := debug.SetPanicOnFault(true) defer debug.SetPanicOnFault(old) + nfault := 0 for _, addr := range faultAddrs { - testSetPanicOnFault(t, uintptr(addr)) + testSetPanicOnFault(t, uintptr(addr), &nfault) + } + if nfault == 0 { + t.Fatalf("none of the addresses faulted") } } -func testSetPanicOnFault(t *testing.T, addr uintptr) { +func testSetPanicOnFault(t *testing.T, addr uintptr, nfault *int) { if GOOS == "nacl" { t.Skip("nacl doesn't seem to fault on high addresses") } defer func() { - if err := recover(); err == nil { - t.Fatalf("did not find error in recover") + if err := recover(); err != nil { + *nfault++ } }() + // The read should fault, except that sometimes we hit + // addresses that have had C or kernel pages mapped there + // readable by user code. So just log the content. + // If no addresses fault, we'll fail the test. var p *int p = (*int)(unsafe.Pointer(addr)) - println(*p) - t.Fatalf("still here - should have faulted on address %#x", addr) + t.Logf("addr %#x: %#x\n", addr, *p) } func eqstring_generic(s1, s2 string) bool { -- cgit v1.3 From 54b63f06478d390d69fc826a16ab19dc6b5d5503 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 21:50:22 -0400 Subject: path/filepath: document that Glob ignores i/o errors Fixes #8008. LGTM=adg R=golang-codereviews, nightlyone, adg CC=golang-codereviews https://golang.org/cl/138630045 --- src/path/filepath/match.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/path/filepath/match.go b/src/path/filepath/match.go index a9bcc103c5..ecc07aa5da 100644 --- a/src/path/filepath/match.go +++ b/src/path/filepath/match.go @@ -228,6 +228,9 @@ func getEsc(chunk string) (r rune, nchunk string, err error) { // as in Match. The pattern may describe hierarchical names such as // /usr/*/bin/ed (assuming the Separator is '/'). // +// Glob ignores file system errors such as I/O errors reading directories. +// The only possible returned error is ErrBadPattern, when pattern +// is malformed. func Glob(pattern string) (matches []string, err error) { if !hasMeta(pattern) { if _, err = os.Lstat(pattern); err != nil { @@ -283,10 +286,7 @@ func glob(dir, pattern string, matches []string) (m []string, e error) { } defer d.Close() - names, err := d.Readdirnames(-1) - if err != nil { - return - } + names, _ := d.Readdirnames(-1) sort.Strings(names) for _, n := range names { -- cgit v1.3 From 66795e8ba101fc20916196db6d343b0d927e7dd5 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Fri, 19 Sep 2014 02:02:21 +0000 Subject: runtime: fix GOARM<7 build Update #8690 If liblink determines that the host doesn't support TLS it replaces the MRC call with a BL runtime.tls_read_fallback. The problem is save_g doesn't expect anyone to make any BL calls and hasn't setup its own link register properly so when runtime.tls_read_fallback returns the LR points to save_g, not save_g's caller so the RET at the end of the function turns into an infinite loop. This fix is only a proof of concept, I think the real fix should go into liblink as its MRC substitution is not as transparent as expected. LGTM=rsc R=rsc, minux CC=golang-codereviews https://golang.org/cl/143050043 --- src/runtime/tls_arm.s | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/runtime/tls_arm.s b/src/runtime/tls_arm.s index 7a247ab195..039b013833 100644 --- a/src/runtime/tls_arm.s +++ b/src/runtime/tls_arm.s @@ -29,7 +29,13 @@ TEXT runtime·save_g(SB),NOSPLIT,$-4 MOVW g, R0 // preserve R0 across call to setg<> RET #endif + // If the host does not support MRC the linker will replace it with + // a call to runtime.read_tls_fallback which jumps to __kuser_get_tls. + // Both functions are written to only disturb R0 so it should be safe to + // use R11 here to temporarily store LR. + MOVW LR, R11 MRC 15, 0, R0, C13, C0, 3 // fetch TLS base pointer + MOVW R11, LR // $runtime.tlsg(SB) is a special linker symbol. // It is the offset from the TLS base pointer to our // thread-local storage for g. @@ -51,7 +57,10 @@ TEXT runtime·load_g(SB),NOSPLIT,$0 // nothing to do as nacl/arm does not use TLS at all. RET #endif + // See comment in save_g. + MOVW LR, R11 MRC 15, 0, R0, C13, C0, 3 // fetch TLS base pointer + MOVW R11, LR // $runtime.tlsg(SB) is a special linker symbol. // It is the offset from the TLS base pointer to our // thread-local storage for g. -- cgit v1.3 From 88d53ddb17d985693b2b5f065286d2647e4cb72d Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 22:33:49 -0400 Subject: runtime: release Windows thread handle in unminit Fixes #8517. LGTM=dvyukov, alex.brainman R=golang-codereviews, dvyukov, alex.brainman CC=golang-codereviews https://golang.org/cl/145890044 --- src/runtime/os_windows.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/runtime/os_windows.c b/src/runtime/os_windows.c index 6c8f137ee5..62d94b65a0 100644 --- a/src/runtime/os_windows.c +++ b/src/runtime/os_windows.c @@ -278,6 +278,8 @@ runtime·minit(void) void runtime·unminit(void) { + runtime·stdcall1(runtime·CloseHandle, (uintptr)g->m->thread); + g->m->thread = nil; } // Described in http://www.dcl.hpi.uni-potsdam.de/research/WRK/2007/08/getting-os-information-the-kuser_shared_data-structure/ -- cgit v1.3 From a07a57b00ec9fdd8f6b02360d39454859709d08a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 23:07:36 -0400 Subject: syscall: mark ECONNRESET, ECONNABORTED as temporary network errors Fixes #6163. LGTM=adg R=golang-codereviews, adg, dvyukov CC=golang-codereviews https://golang.org/cl/141600043 --- src/syscall/syscall_unix.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/syscall/syscall_unix.go b/src/syscall/syscall_unix.go index f18dfca5e6..a06bd7dd08 100644 --- a/src/syscall/syscall_unix.go +++ b/src/syscall/syscall_unix.go @@ -109,7 +109,7 @@ func (e Errno) Error() string { } func (e Errno) Temporary() bool { - return e == EINTR || e == EMFILE || e.Timeout() + return e == EINTR || e == EMFILE || e == ECONNRESET || e == ECONNABORTED || e.Timeout() } func (e Errno) Timeout() bool { -- cgit v1.3 From c7f6bd795acf002d60f712f0f4e2701051e74e4a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 18 Sep 2014 23:51:22 -0400 Subject: runtime: rename SchedType to SchedT CL 144940043 renamed it from Sched to SchedType to avoid a lowercasing conflict in the Go code with the variable named sched. We've been using just T resolve those conflicts, not Type. The FooType pattern is already taken for the kind-specific variants of the runtime Type structure: ChanType, MapType, and so on. SchedType isn't a Type. LGTM=bradfitz, khr R=khr, bradfitz CC=golang-codereviews https://golang.org/cl/145180043 --- src/runtime/proc.c | 2 +- src/runtime/runtime.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/runtime/proc.c b/src/runtime/proc.c index 860701ee58..3f4179d473 100644 --- a/src/runtime/proc.c +++ b/src/runtime/proc.c @@ -31,7 +31,7 @@ enum GoidCacheBatch = 16, }; -SchedType runtime·sched; +SchedT runtime·sched; int32 runtime·gomaxprocs; uint32 runtime·needextram; bool runtime·iscgo; diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 386b09b96b..7fefbc2997 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -60,7 +60,7 @@ typedef struct SudoG SudoG; typedef struct Mutex Mutex; typedef struct M M; typedef struct P P; -typedef struct SchedType SchedType; +typedef struct SchedT SchedT; typedef struct Note Note; typedef struct Slice Slice; typedef struct String String; @@ -434,7 +434,7 @@ enum { MaxGomaxprocs = 1<<8, }; -struct SchedType +struct SchedT { Mutex lock; @@ -753,7 +753,7 @@ extern DebugVars runtime·debug; extern uintptr runtime·maxstacksize; extern Note runtime·signote; extern ForceGCState runtime·forcegc; -extern SchedType runtime·sched; +extern SchedT runtime·sched; extern int32 runtime·newprocs; /* -- cgit v1.3 From 0c47bd1e61ab09e04572170f839297cb8ce97a5c Mon Sep 17 00:00:00 2001 From: John Tuley Date: Fri, 19 Sep 2014 11:28:38 -0400 Subject: net/http: ensured that proxy errors are returned by Transport.RoundTrip. Fixes #8755. LGTM=bradfitz R=bradfitz CC=golang-codereviews, jtuley https://golang.org/cl/136710044 --- src/net/http/transport.go | 2 +- src/net/http/transport_test.go | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/net/http/transport.go b/src/net/http/transport.go index 527ed8bdd1..f1aab8587c 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -316,7 +316,7 @@ func (t *Transport) connectMethodForRequest(treq *transportRequest) (cm connectM if t.Proxy != nil { cm.proxyURL, err = t.Proxy(treq.Request) } - return cm, nil + return cm, err } // proxyAuth returns the Proxy-Authorization header to set diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 3460d690e3..bdfeba3626 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -2136,6 +2136,24 @@ func TestTransportDialTLS(t *testing.T) { } } +// Test for issue 8755 +// Ensure that if a proxy returns an error, it is exposed by RoundTrip +func TestRoundTripReturnsProxyError(t *testing.T) { + badProxy := func(*http.Request) (*url.URL, error) { + return nil, errors.New("errorMessage") + } + + tr := &Transport{Proxy: badProxy} + + req, _ := http.NewRequest("GET", "http://example.com", nil) + + _, err := tr.RoundTrip(req) + + if err == nil { + t.Error("Expected proxy error to be returned by RoundTrip") + } +} + func wantBody(res *http.Response, err error, want string) error { if err != nil { return err -- cgit v1.3 From 182d1316dd975f426451cee34ba2e3e0953e084f Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 19 Sep 2014 13:51:06 -0400 Subject: cmd/go, testing: add TestMain support Fixes #8202. LGTM=r, bradfitz R=r, josharian, bradfitz CC=golang-codereviews https://golang.org/cl/148770043 --- doc/go1.4.txt | 1 + src/cmd/go/test.go | 46 +++++++++++++++++++++++++++++++++++-- src/testing/testing.go | 56 +++++++++++++++++++++++++++++++++++++++++---- src/testing/testing_test.go | 18 +++++++++++++++ 4 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 src/testing/testing_test.go (limited to 'src') diff --git a/doc/go1.4.txt b/doc/go1.4.txt index 6180bc5b92..f46ef48f5d 100644 --- a/doc/go1.4.txt +++ b/doc/go1.4.txt @@ -32,6 +32,7 @@ sync/atomic: add Value (CL 136710045) syscall: Setuid, Setgid are disabled on linux platforms. On linux those syscalls operate on the calling thread, not the whole process. This does not match the semantics of other platforms, nor the expectations of the caller, so the operations have been disabled until issue 1435 is resolved (CL 106170043) syscall: now frozen (CL 129820043) testing: add Coverage (CL 98150043) +testing: add TestMain support (CL 148770043) text/scanner: add IsIdentRune field of Scanner. (CL 108030044) time: use the micro symbol (µ (U+00B5)) to print microsecond duration (CL 105030046) encoding/asn1: optional elements with a default value will now only be omitted if they have that value (CL 86960045). diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index a602469e44..e990b17bfa 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -6,6 +6,7 @@ package main import ( "bytes" + "errors" "fmt" "go/ast" "go/build" @@ -291,6 +292,7 @@ var testMainDeps = map[string]bool{ // Dependencies for testmain. "testing": true, "regexp": true, + "os": true, } func runTest(cmd *Command, args []string) { @@ -687,7 +689,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, omitDWARF: !testC && !testNeedBinary, } - // The generated main also imports testing and regexp. + // The generated main also imports testing, regexp, and os. stk.push("testmain") for dep := range testMainDeps { if dep == ptest.ImportPath { @@ -1057,6 +1059,31 @@ func (b *builder) notest(a *action) error { return nil } +// isTestMain tells whether fn is a TestMain(m *testing.Main) function. +func isTestMain(fn *ast.FuncDecl) bool { + if fn.Name.String() != "TestMain" || + fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || + fn.Type.Params == nil || + len(fn.Type.Params.List) != 1 || + len(fn.Type.Params.List[0].Names) > 1 { + return false + } + ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr) + if !ok { + return false + } + // We can't easily check that the type is *testing.M + // because we don't know how testing has been imported, + // but at least check that it's *M or *something.M. + if name, ok := ptr.X.(*ast.Ident); ok && name.Name == "M" { + return true + } + if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == "M" { + return true + } + return false +} + // isTest tells whether name looks like a test (or benchmark, according to prefix). // It is a Test (say) if there is a character after Test that is not a lower-case letter. // We don't want TesticularCancer. @@ -1113,6 +1140,7 @@ type testFuncs struct { Tests []testFunc Benchmarks []testFunc Examples []testFunc + TestMain *testFunc Package *Package ImportTest bool NeedTest bool @@ -1168,6 +1196,12 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { } name := n.Name.String() switch { + case isTestMain(n): + if t.TestMain != nil { + return errors.New("multiple definitions of TestMain") + } + t.TestMain = &testFunc{pkg, name, ""} + *doImport, *seen = true, true case isTest(name, "Test"): t.Tests = append(t.Tests, testFunc{pkg, name, ""}) *doImport, *seen = true, true @@ -1200,6 +1234,9 @@ var testmainTmpl = template.Must(template.New("main").Parse(` package main import ( +{{if not .TestMain}} + "os" +{{end}} "regexp" "testing" @@ -1294,7 +1331,12 @@ func main() { CoveredPackages: {{printf "%q" .Covered}}, }) {{end}} - testing.Main(matchString, tests, benchmarks, examples) + m := testing.MainStart(matchString, tests, benchmarks, examples) +{{with .TestMain}} + {{.Package}}.{{.Name}}(m) +{{else}} + os.Exit(m.Run()) +{{end}} } `)) diff --git a/src/testing/testing.go b/src/testing/testing.go index 731762cb1d..21460b0ed4 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -117,6 +117,26 @@ // The entire test file is presented as the example when it contains a single // example function, at least one other function, type, variable, or constant // declaration, and no test or benchmark functions. +// +// Main +// +// It is sometimes necessary for a test program to do extra setup or teardown +// before or after testing. It is also sometimes necessary for a test to control +// which code runs on the main thread. To support these and other cases, +// if a test file contains a function: +// +// func TestMain(m *testing.M) +// +// then the generated test will call TestMain(m) instead of running the tests +// directly. TestMain runs in the main goroutine and can do whatever setup +// and teardown is necessary around a call to m.Run. It should then call +// os.Exit with the result of m.Run. +// +// The minimal implementation of TestMain is: +// +// func TestMain(m *testing.M) { os.Exit(m.Run()) } +// +// In effect, that is the implementation used when no TestMain is explicitly defined. package testing import ( @@ -431,23 +451,49 @@ func tRunner(t *T, test *InternalTest) { // An internal function but exported because it is cross-package; part of the implementation // of the "go test" command. func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) { + os.Exit(MainStart(matchString, tests, benchmarks, examples).Run()) +} + +// M is a type passed to a TestMain function to run the actual tests. +type M struct { + matchString func(pat, str string) (bool, error) + tests []InternalTest + benchmarks []InternalBenchmark + examples []InternalExample +} + +// MainStart is meant for use by tests generated by 'go test'. +// It is not meant to be called directly and is not subject to the Go 1 compatibility document. +// It may change signature from release to release. +func MainStart(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M { + return &M{ + matchString: matchString, + tests: tests, + benchmarks: benchmarks, + examples: examples, + } +} + +// Run runs the tests. It returns an exit code to pass to os.Exit. +func (m *M) Run() int { flag.Parse() parseCpuList() before() startAlarm() - haveExamples = len(examples) > 0 - testOk := RunTests(matchString, tests) - exampleOk := RunExamples(matchString, examples) + haveExamples = len(m.examples) > 0 + testOk := RunTests(m.matchString, m.tests) + exampleOk := RunExamples(m.matchString, m.examples) stopAlarm() if !testOk || !exampleOk { fmt.Println("FAIL") after() - os.Exit(1) + return 1 } fmt.Println("PASS") - RunBenchmarks(matchString, benchmarks) + RunBenchmarks(m.matchString, m.benchmarks) after() + return 0 } func (t *T) report() { diff --git a/src/testing/testing_test.go b/src/testing/testing_test.go new file mode 100644 index 0000000000..87a5c16d6e --- /dev/null +++ b/src/testing/testing_test.go @@ -0,0 +1,18 @@ +// Copyright 2014 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 testing_test + +import ( + "os" + "testing" +) + +// This is exactly what a test would do without a TestMain. +// It's here only so that there is at least one package in the +// standard library with a TestMain, so that code is executed. + +func TestMain(m *testing.M) { + os.Exit(m.Run()) +} -- cgit v1.3 From 5c795632d658003bdc193b0441137385950bca54 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 19 Sep 2014 13:51:23 -0400 Subject: runtime: add runtime· prefix to some static variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pure renaming. This will make an upcoming CL have smaller diffs. LGTM=dvyukov, iant R=iant, dvyukov CC=golang-codereviews https://golang.org/cl/142280043 --- src/runtime/mcache.c | 10 ++--- src/runtime/mgc0.c | 112 +++++++++++++++++++++++++-------------------------- src/runtime/stack.c | 30 +++++++------- 3 files changed, 76 insertions(+), 76 deletions(-) (limited to 'src') diff --git a/src/runtime/mcache.c b/src/runtime/mcache.c index bb1fc54032..17ea5d2e26 100644 --- a/src/runtime/mcache.c +++ b/src/runtime/mcache.c @@ -13,7 +13,7 @@ extern volatile intgo runtime·MemProfileRate; // dummy MSpan that contains no free objects. -static MSpan emptymspan; +static MSpan runtime·emptymspan; MCache* runtime·allocmcache(void) @@ -27,7 +27,7 @@ runtime·allocmcache(void) runtime·unlock(&runtime·mheap.lock); runtime·memclr((byte*)c, sizeof(*c)); for(i = 0; i < NumSizeClasses; i++) - c->alloc[i] = &emptymspan; + c->alloc[i] = &runtime·emptymspan; // Set first allocation sample size. rate = runtime·MemProfileRate; @@ -83,7 +83,7 @@ runtime·MCache_Refill(MCache *c, int32 sizeclass) s = c->alloc[sizeclass]; if(s->freelist != nil) runtime·throw("refill on a nonempty span"); - if(s != &emptymspan) + if(s != &runtime·emptymspan) s->incache = false; // Get a new cached span from the central lists. @@ -107,9 +107,9 @@ runtime·MCache_ReleaseAll(MCache *c) for(i=0; ialloc[i]; - if(s != &emptymspan) { + if(s != &runtime·emptymspan) { runtime·MCentral_UncacheSpan(&runtime·mheap.central[i].mcentral, s); - c->alloc[i] = &emptymspan; + c->alloc[i] = &runtime·emptymspan; } } } diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c index 88c8d0f3c9..03ca288496 100644 --- a/src/runtime/mgc0.c +++ b/src/runtime/mgc0.c @@ -120,7 +120,7 @@ FinBlock* runtime·finc; // cache of free blocks static byte finptrmask[FinBlockSize/PtrSize/PointersPerByte]; bool runtime·fingwait; bool runtime·fingwake; -static FinBlock *allfin; // list of all blocks +static FinBlock *runtime·allfin; // list of all blocks BitVector runtime·gcdatamask; BitVector runtime·gcbssmask; @@ -154,7 +154,7 @@ static struct { // Copy of mheap.allspans for marker or sweeper. MSpan** spans; uint32 nspan; -} work; +} runtime·work; // scanblock scans a block of n bytes starting at pointer b for references // to other objects, scanning any it finds recursively until there are no @@ -225,7 +225,7 @@ scanblock(byte *b, uintptr n, byte *ptrmask) } // If another proc wants a pointer, give it some. - if(work.nwait > 0 && nobj > 4 && work.full == 0) { + if(runtime·work.nwait > 0 && nobj > 4 && runtime·work.full == 0) { wbuf->nobj = nobj; wbuf = handoff(wbuf); nobj = wbuf->nobj; @@ -369,7 +369,7 @@ scanblock(byte *b, uintptr n, byte *ptrmask) // quadruple is already marked. Otherwise we resort to CAS // loop for marking. if((xbits&(bitMask|(bitMask<alllink) + for(fb=runtime·allfin; fb; fb=fb->alllink) scanblock((byte*)fb->fin, fb->cnt*sizeof(fb->fin[0]), finptrmask); break; case RootSpans: // mark MSpan.specials sg = runtime·mheap.sweepgen; - for(spanidx=0; spanidxstate != MSpanInUse) continue; if(s->sweepgen != sg) { @@ -479,7 +479,7 @@ markroot(ParFor *desc, uint32 i) // needed only to output in traceback status = runtime·readgstatus(gp); if((status == Gwaiting || status == Gsyscall) && gp->waitsince == 0) - gp->waitsince = work.tstart; + gp->waitsince = runtime·work.tstart; // Shrink a stack if not much of it is being used. runtime·shrinkstack(gp); if(runtime·readgstatus(gp) == Gdead) @@ -502,7 +502,7 @@ getempty(Workbuf *b) MCache *c; if(b != nil) - runtime·lfstackpush(&work.full, &b->node); + runtime·lfstackpush(&runtime·work.full, &b->node); b = nil; c = g->m->mcache; if(c->gcworkbuf != nil) { @@ -510,7 +510,7 @@ getempty(Workbuf *b) c->gcworkbuf = nil; } if(b == nil) - b = (Workbuf*)runtime·lfstackpop(&work.empty); + b = (Workbuf*)runtime·lfstackpop(&runtime·work.empty); if(b == nil) b = runtime·persistentalloc(sizeof(*b), CacheLineSize, &mstats.gc_sys); b->nobj = 0; @@ -527,7 +527,7 @@ putempty(Workbuf *b) c->gcworkbuf = b; return; } - runtime·lfstackpush(&work.empty, &b->node); + runtime·lfstackpush(&runtime·work.empty, &b->node); } void @@ -544,21 +544,21 @@ getfull(Workbuf *b) int32 i; if(b != nil) - runtime·lfstackpush(&work.empty, &b->node); - b = (Workbuf*)runtime·lfstackpop(&work.full); - if(b != nil || work.nproc == 1) + runtime·lfstackpush(&runtime·work.empty, &b->node); + b = (Workbuf*)runtime·lfstackpop(&runtime·work.full); + if(b != nil || runtime·work.nproc == 1) return b; - runtime·xadd(&work.nwait, +1); + runtime·xadd(&runtime·work.nwait, +1); for(i=0;; i++) { - if(work.full != 0) { - runtime·xadd(&work.nwait, -1); - b = (Workbuf*)runtime·lfstackpop(&work.full); + if(runtime·work.full != 0) { + runtime·xadd(&runtime·work.nwait, -1); + b = (Workbuf*)runtime·lfstackpop(&runtime·work.full); if(b != nil) return b; - runtime·xadd(&work.nwait, +1); + runtime·xadd(&runtime·work.nwait, +1); } - if(work.nwait == work.nproc) + if(runtime·work.nwait == runtime·work.nproc) return nil; if(i < 10) { g->m->gcstats.nprocyield++; @@ -589,7 +589,7 @@ handoff(Workbuf *b) g->m->gcstats.nhandoffcnt += n; // Put b on full list - let first half of b get stolen. - runtime·lfstackpush(&work.full, &b->node); + runtime·lfstackpush(&runtime·work.full, &b->node); return b1; } @@ -773,8 +773,8 @@ runtime·queuefinalizer(byte *p, FuncVal *fn, uintptr nret, Type *fint, PtrType if(runtime·finc == nil) { runtime·finc = runtime·persistentalloc(FinBlockSize, 0, &mstats.gc_sys); runtime·finc->cap = (FinBlockSize - sizeof(FinBlock)) / sizeof(Finalizer) + 1; - runtime·finc->alllink = allfin; - allfin = runtime·finc; + runtime·finc->alllink = runtime·allfin; + runtime·allfin = runtime·finc; if(finptrmask[0] == 0) { // Build pointer mask for Finalizer array in block. // Check assumptions made in finalizer1 array above. @@ -814,7 +814,7 @@ runtime·iterate_finq(void (*callback)(FuncVal*, byte*, uintptr, Type*, PtrType* Finalizer *f; uintptr i; - for(fb = allfin; fb; fb = fb->alllink) { + for(fb = runtime·allfin; fb; fb = fb->alllink) { for(i = 0; i < fb->cnt; i++) { f = &fb->fin[i]; callback(f->fn, f->arg, f->nret, f->fint, f->ot); @@ -1065,12 +1065,12 @@ runtime·sweepone(void) sg = runtime·mheap.sweepgen; for(;;) { idx = runtime·xadd(&runtime·sweep.spanidx, 1) - 1; - if(idx >= work.nspan) { + if(idx >= runtime·work.nspan) { runtime·mheap.sweepdone = true; g->m->locks--; return -1; } - s = work.spans[idx]; + s = runtime·work.spans[idx]; if(s->state != MSpanInUse) { s->sweepgen = sg; continue; @@ -1118,14 +1118,14 @@ runtime·gchelper(void) gchelperstart(); // parallel mark for over gc roots - runtime·parfordo(work.markfor); + runtime·parfordo(runtime·work.markfor); // help other threads scan secondary blocks scanblock(nil, 0, nil); - nproc = work.nproc; // work.nproc can change right after we increment work.ndone - if(runtime·xadd(&work.ndone, +1) == nproc-1) - runtime·notewakeup(&work.alldone); + nproc = runtime·work.nproc; // runtime·work.nproc can change right after we increment runtime·work.ndone + if(runtime·xadd(&runtime·work.ndone, +1) == nproc-1) + runtime·notewakeup(&runtime·work.alldone); g->m->traceback = 0; } @@ -1284,7 +1284,7 @@ runtime·gcinit(void) if(sizeof(Workbuf) != WorkbufSize) runtime·throw("runtime: size of Workbuf is suboptimal"); - work.markfor = runtime·parforalloc(MaxGcproc); + runtime·work.markfor = runtime·parforalloc(MaxGcproc); runtime·gcpercent = runtime·readgogc(); runtime·gcdatamask = unrollglobgcprog(runtime·gcdata, runtime·edata - runtime·data); runtime·gcbssmask = unrollglobgcprog(runtime·gcbss, runtime·ebss - runtime·bss); @@ -1319,7 +1319,7 @@ gc(struct gc_args *args) g->m->traceback = 2; t0 = args->start_time; - work.tstart = args->start_time; + runtime·work.tstart = args->start_time; t1 = 0; if(runtime·debug.gctrace) @@ -1339,21 +1339,21 @@ gc(struct gc_args *args) // Even if this is stop-the-world, a concurrent exitsyscall can allocate a stack from heap. runtime·lock(&runtime·mheap.lock); // Free the old cached sweep array if necessary. - if(work.spans != nil && work.spans != runtime·mheap.allspans) - runtime·SysFree(work.spans, work.nspan*sizeof(work.spans[0]), &mstats.other_sys); + if(runtime·work.spans != nil && runtime·work.spans != runtime·mheap.allspans) + runtime·SysFree(runtime·work.spans, runtime·work.nspan*sizeof(runtime·work.spans[0]), &mstats.other_sys); // Cache the current array for marking. runtime·mheap.gcspans = runtime·mheap.allspans; - work.spans = runtime·mheap.allspans; - work.nspan = runtime·mheap.nspan; + runtime·work.spans = runtime·mheap.allspans; + runtime·work.nspan = runtime·mheap.nspan; runtime·unlock(&runtime·mheap.lock); - work.nwait = 0; - work.ndone = 0; - work.nproc = runtime·gcprocs(); - runtime·parforsetup(work.markfor, work.nproc, RootCount + runtime·allglen, nil, false, markroot); - if(work.nproc > 1) { - runtime·noteclear(&work.alldone); - runtime·helpgc(work.nproc); + runtime·work.nwait = 0; + runtime·work.ndone = 0; + runtime·work.nproc = runtime·gcprocs(); + runtime·parforsetup(runtime·work.markfor, runtime·work.nproc, RootCount + runtime·allglen, nil, false, markroot); + if(runtime·work.nproc > 1) { + runtime·noteclear(&runtime·work.alldone); + runtime·helpgc(runtime·work.nproc); } t2 = 0; @@ -1361,15 +1361,15 @@ gc(struct gc_args *args) t2 = runtime·nanotime(); gchelperstart(); - runtime·parfordo(work.markfor); + runtime·parfordo(runtime·work.markfor); scanblock(nil, 0, nil); t3 = 0; if(runtime·debug.gctrace) t3 = runtime·nanotime(); - if(work.nproc > 1) - runtime·notesleep(&work.alldone); + if(runtime·work.nproc > 1) + runtime·notesleep(&runtime·work.alldone); cachestats(); // next_gc calculation is tricky with concurrent sweep since we don't know size of live heap @@ -1396,21 +1396,21 @@ gc(struct gc_args *args) } obj = mstats.nmalloc - mstats.nfree; - stats.nprocyield += work.markfor->nprocyield; - stats.nosyield += work.markfor->nosyield; - stats.nsleep += work.markfor->nsleep; + stats.nprocyield += runtime·work.markfor->nprocyield; + stats.nosyield += runtime·work.markfor->nosyield; + stats.nsleep += runtime·work.markfor->nsleep; runtime·printf("gc%d(%d): %D+%D+%D+%D us, %D -> %D MB, %D (%D-%D) objects," " %d goroutines," " %d/%d/%d sweeps," " %D(%D) handoff, %D(%D) steal, %D/%D/%D yields\n", - mstats.numgc, work.nproc, (t1-t0)/1000, (t2-t1)/1000, (t3-t2)/1000, (t4-t3)/1000, + mstats.numgc, runtime·work.nproc, (t1-t0)/1000, (t2-t1)/1000, (t3-t2)/1000, (t4-t3)/1000, heap0>>20, heap1>>20, obj, mstats.nmalloc, mstats.nfree, runtime·gcount(), - work.nspan, runtime·sweep.nbgsweep, runtime·sweep.npausesweep, + runtime·work.nspan, runtime·sweep.nbgsweep, runtime·sweep.npausesweep, stats.nhandoff, stats.nhandoffcnt, - work.markfor->nsteal, work.markfor->nstealcnt, + runtime·work.markfor->nsteal, runtime·work.markfor->nstealcnt, stats.nprocyield, stats.nosyield, stats.nsleep); runtime·sweep.nbgsweep = runtime·sweep.npausesweep = 0; } @@ -1419,14 +1419,14 @@ gc(struct gc_args *args) // Even if this is still stop-the-world, a concurrent exitsyscall can allocate a stack from heap. runtime·lock(&runtime·mheap.lock); // Free the old cached mark array if necessary. - if(work.spans != nil && work.spans != runtime·mheap.allspans) - runtime·SysFree(work.spans, work.nspan*sizeof(work.spans[0]), &mstats.other_sys); + if(runtime·work.spans != nil && runtime·work.spans != runtime·mheap.allspans) + runtime·SysFree(runtime·work.spans, runtime·work.nspan*sizeof(runtime·work.spans[0]), &mstats.other_sys); // Cache the current array for sweeping. runtime·mheap.gcspans = runtime·mheap.allspans; runtime·mheap.sweepgen += 2; runtime·mheap.sweepdone = false; - work.spans = runtime·mheap.allspans; - work.nspan = runtime·mheap.nspan; + runtime·work.spans = runtime·mheap.allspans; + runtime·work.nspan = runtime·mheap.nspan; runtime·sweep.spanidx = 0; runtime·unlock(&runtime·mheap.lock); diff --git a/src/runtime/stack.c b/src/runtime/stack.c index 95a5a123d9..6fbab8fb6e 100644 --- a/src/runtime/stack.c +++ b/src/runtime/stack.c @@ -32,8 +32,8 @@ enum // Stacks are assigned an order according to size. // order = log_2(size/FixedStack) // There is a free list for each order. -static MSpan stackpool[NumStackOrders]; -static Mutex stackpoolmu; +static MSpan runtime·stackpool[NumStackOrders]; +static Mutex runtime·stackpoolmu; // TODO: one lock per order? void @@ -45,7 +45,7 @@ runtime·stackinit(void) runtime·throw("cache size must be a multiple of page size"); for(i = 0; i < NumStackOrders; i++) - runtime·MSpanList_Init(&stackpool[i]); + runtime·MSpanList_Init(&runtime·stackpool[i]); } // Allocates a stack from the free pool. Must be called with @@ -58,7 +58,7 @@ poolalloc(uint8 order) MLink *x; uintptr i; - list = &stackpool[order]; + list = &runtime·stackpool[order]; s = list->next; if(s == list) { // no free stacks. Allocate another span worth. @@ -99,7 +99,7 @@ poolfree(MLink *x, uint8 order) runtime·throw("freeing stack not in a stack span"); if(s->freelist == nil) { // s will now have a free stack - runtime·MSpanList_Insert(&stackpool[order], s); + runtime·MSpanList_Insert(&runtime·stackpool[order], s); } x->next = s->freelist; s->freelist = x; @@ -127,14 +127,14 @@ stackcacherefill(MCache *c, uint8 order) // Grab half of the allowed capacity (to prevent thrashing). list = nil; size = 0; - runtime·lock(&stackpoolmu); + runtime·lock(&runtime·stackpoolmu); while(size < StackCacheSize/2) { x = poolalloc(order); x->next = list; list = x; size += FixedStack << order; } - runtime·unlock(&stackpoolmu); + runtime·unlock(&runtime·stackpoolmu); c->stackcache[order].list = list; c->stackcache[order].size = size; @@ -150,14 +150,14 @@ stackcacherelease(MCache *c, uint8 order) runtime·printf("stackcacherelease order=%d\n", order); x = c->stackcache[order].list; size = c->stackcache[order].size; - runtime·lock(&stackpoolmu); + runtime·lock(&runtime·stackpoolmu); while(size > StackCacheSize/2) { y = x->next; poolfree(x, order); x = y; size -= FixedStack << order; } - runtime·unlock(&stackpoolmu); + runtime·unlock(&runtime·stackpoolmu); c->stackcache[order].list = x; c->stackcache[order].size = size; } @@ -170,7 +170,7 @@ runtime·stackcache_clear(MCache *c) if(StackDebug >= 1) runtime·printf("stackcache clear\n"); - runtime·lock(&stackpoolmu); + runtime·lock(&runtime·stackpoolmu); for(order = 0; order < NumStackOrders; order++) { x = c->stackcache[order].list; while(x != nil) { @@ -181,7 +181,7 @@ runtime·stackcache_clear(MCache *c) c->stackcache[order].list = nil; c->stackcache[order].size = 0; } - runtime·unlock(&stackpoolmu); + runtime·unlock(&runtime·stackpoolmu); } Stack @@ -227,9 +227,9 @@ runtime·stackalloc(uint32 n) // procresize. Just get a stack from the global pool. // Also don't touch stackcache during gc // as it's flushed concurrently. - runtime·lock(&stackpoolmu); + runtime·lock(&runtime·stackpoolmu); x = poolalloc(order); - runtime·unlock(&stackpoolmu); + runtime·unlock(&runtime·stackpoolmu); } else { x = c->stackcache[order].list; if(x == nil) { @@ -289,9 +289,9 @@ runtime·stackfree(Stack stk) x = (MLink*)v; c = g->m->mcache; if(c == nil || g->m->gcing || g->m->helpgc) { - runtime·lock(&stackpoolmu); + runtime·lock(&runtime·stackpoolmu); poolfree(x, order); - runtime·unlock(&stackpoolmu); + runtime·unlock(&runtime·stackpoolmu); } else { if(c->stackcache[order].size >= StackCacheSize) stackcacherelease(c, order); -- cgit v1.3 From 2c15d45131bbad55c710427a2ea5a1b383e9811c Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 19 Sep 2014 13:53:33 -0400 Subject: net/http: document server recovering panics Fixes #8594. LGTM=bradfitz R=bradfitz CC=golang-codereviews https://golang.org/cl/145760043 --- src/net/http/server.go | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/net/http/server.go b/src/net/http/server.go index 203037e9f5..8f2b777b29 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -42,6 +42,12 @@ var ( // and then return. Returning signals that the request is finished // and that the HTTP server can move on to the next request on // the connection. +// +// If ServeHTTP panics, the server (the caller of ServeHTTP) assumes +// that the effect of the panic was isolated to the active request. +// It recovers the panic, logs a stack trace to the server error log, +// and hangs up the connection. +// type Handler interface { ServeHTTP(ResponseWriter, *Request) } -- cgit v1.3 From 0e1a07167b2ba0e71487feea8e2e20a2e29fdf3a Mon Sep 17 00:00:00 2001 From: Sameer Ajmani Date: Fri, 19 Sep 2014 15:59:47 -0400 Subject: cmd/go: fix typo LGTM=rsc R=rsc CC=golang-codereviews https://golang.org/cl/138700043 --- src/cmd/go/test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index e990b17bfa..c135b89c84 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -49,7 +49,7 @@ It prints a summary of the test results in the format: followed by detailed output for each failed package. 'Go test' recompiles each package along with any files with names matching -the file pattern "*_test.go". +the file pattern "*_test.go". Files whose names begin with "_" (including "_test.go") or "." are ignored. These additional files can contain test functions, benchmark functions, and example functions. See 'go help testfunc' for more. @@ -1059,7 +1059,7 @@ func (b *builder) notest(a *action) error { return nil } -// isTestMain tells whether fn is a TestMain(m *testing.Main) function. +// isTestMain tells whether fn is a TestMain(m *testing.M) function. func isTestMain(fn *ast.FuncDecl) bool { if fn.Name.String() != "TestMain" || fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || -- cgit v1.3 From 0306478fe57767530164b43c12969ca91496db47 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Fri, 19 Sep 2014 16:33:14 -0700 Subject: runtime: Fix interaction between Goexit and defers When running defers, we must check whether the defer has already been marked as started so we don't run it twice. Fixes #8774. LGTM=rsc R=rsc CC=golang-codereviews https://golang.org/cl/142280044 --- src/runtime/crash_test.go | 88 +++++++++++++++++++++++++++++++++++++++++++++++ src/runtime/panic.go | 18 +++++++++- 2 files changed, 105 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index d1577fb5fe..783b4c48f5 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -412,3 +412,91 @@ func main() { runtime.Breakpoint() } ` + +func TestGoexitInPanic(t *testing.T) { + // see issue 8774: this code used to trigger an infinite recursion + output := executeTest(t, goexitInPanicSource, nil) + want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" + if !strings.HasPrefix(output, want) { + t.Fatalf("output does not start with %q:\n%s", want, output) + } +} + +const goexitInPanicSource = ` +package main +import "runtime" +func main() { + go func() { + defer func() { + runtime.Goexit() + }() + panic("hello") + }() + runtime.Goexit() +} +` + +func TestPanicAfterGoexit(t *testing.T) { + // an uncaught panic should still work after goexit + output := executeTest(t, panicAfterGoexitSource, nil) + want := "panic: hello" + if !strings.HasPrefix(output, want) { + t.Fatalf("output does not start with %q:\n%s", want, output) + } +} + +const panicAfterGoexitSource = ` +package main +import "runtime" +func main() { + defer func() { + panic("hello") + }() + runtime.Goexit() +} +` + +func TestRecoveredPanicAfterGoexit(t *testing.T) { + output := executeTest(t, recoveredPanicAfterGoexitSource, nil) + want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" + if !strings.HasPrefix(output, want) { + t.Fatalf("output does not start with %q:\n%s", want, output) + } +} + +const recoveredPanicAfterGoexitSource = ` +package main +import "runtime" +func main() { + defer func() { + defer func() { + r := recover() + if r == nil { + panic("bad recover") + } + }() + panic("hello") + }() + runtime.Goexit() +} +` + +func TestRecoverBeforePanicAfterGoexit(t *testing.T) { + // 1. defer a function that recovers + // 2. defer a function that panics + // 3. call goexit + // Goexit should run the #2 defer. Its panic + // should be caught by the #1 defer, and execution + // should resume in the caller. Like the Goexit + // never happened! + defer func() { + r := recover() + if r == nil { + panic("bad recover") + } + }() + defer func() { + panic("hello") + }() + runtime.Goexit() +} diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 3cc31053e8..7eb2d6055a 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -247,11 +247,27 @@ func deferreturn(arg0 uintptr) { // If all other goroutines exit, the program crashes. func Goexit() { // Run all deferred functions for the current goroutine. + // This code is similar to gopanic, see that implementation + // for detailed comments. gp := getg() - for gp._defer != nil { + for { d := gp._defer + if d == nil { + break + } + if d.started { + if d._panic != nil { + d._panic.aborted = true + } + gp._defer = d.link + freedefer(d) + continue + } d.started = true reflectcall(unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz)) + if gp._defer != d { + gothrow("bad defer entry in Goexit") + } gp._defer = d.link freedefer(d) // Note: we ignore recovers here because Goexit isn't a panic -- cgit v1.3 From 3b2577ced39194fdd2f1359136c7e176d8de5576 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Sat, 20 Sep 2014 23:31:11 -0700 Subject: runtime: be very careful with bad pointer tests Saw this on a test: runtime: bad pointer in frame runtime_test.testSetPanicOnFault at 0xc20801c6b0: 0xfff fatal error: bad pointer! runtime stack: ... copystack(0xc2081bf7a0, 0x1000) /root/work/solaris-amd64-smartos-2dde8b453d26/go/src/runtime/stack.c:621 +0x173 fp=0xfffffd7ffd5ffee0 sp=0xfffffd7ffd5ffe20 runtime.newstack() /root/work/solaris-amd64-smartos-2dde8b453d26/go/src/runtime/stack.c:774 +0x552 fp=0xfffffd7ffd5fff90 sp=0xfffffd7ffd5ffee0 runtime.morestack() /root/work/solaris-amd64-smartos-2dde8b453d26/go/src/runtime/asm_amd64.s:324 +0x90 fp=0xfffffd7ffd5fff98 sp=0xfffffd7ffd5fff90 goroutine 163354 [stack growth]: ... runtime.convT2E(0x587000, 0xc20807bea8, 0x0, 0x0) /root/work/solaris-amd64-smartos-2dde8b453d26/go/src/runtime/iface.go:141 +0xd2 fp=0xc20801c678 sp=0xc20801c640 runtime_test.testSetPanicOnFault(0xc20822c510, 0xfff, 0xc20801c748) /root/work/solaris-amd64-smartos-2dde8b453d26/go/src/runtime/runtime_test.go:211 +0xc6 fp=0xc20801c718 sp=0xc20801c678 ... This test is testing bad pointers. It loads the bad pointer into a pointer variable, but before it gets a chance to dereference it, calls convT2E. That call causes a stack copy, which exposes that live but bad pointer variable. LGTM=dvyukov R=golang-codereviews, dvyukov CC=golang-codereviews https://golang.org/cl/146880043 --- src/runtime/runtime_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/runtime/runtime_test.go b/src/runtime/runtime_test.go index 3c4075842b..1688364a8e 100644 --- a/src/runtime/runtime_test.go +++ b/src/runtime/runtime_test.go @@ -206,9 +206,8 @@ func testSetPanicOnFault(t *testing.T, addr uintptr, nfault *int) { // addresses that have had C or kernel pages mapped there // readable by user code. So just log the content. // If no addresses fault, we'll fail the test. - var p *int - p = (*int)(unsafe.Pointer(addr)) - t.Logf("addr %#x: %#x\n", addr, *p) + v := *(*byte)(unsafe.Pointer(addr)) + t.Logf("addr %#x: %#x\n", addr, v) } func eqstring_generic(s1, s2 string) bool { -- cgit v1.3 From 0be3176a8bc394f5234a2665843b5490e9095f46 Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Mon, 22 Sep 2014 14:29:45 +1000 Subject: image/gif: don't let the per-frame transparent index modify the global palette. Fixes #7993. LGTM=r R=r CC=golang-codereviews, james.jdunne https://golang.org/cl/138600043 --- src/image/gif/reader.go | 7 ++++- src/image/gif/reader_test.go | 64 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 61 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/image/gif/reader.go b/src/image/gif/reader.go index 926710a456..5a863e204f 100644 --- a/src/image/gif/reader.go +++ b/src/image/gif/reader.go @@ -171,7 +171,8 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { if err != nil { return err } - if d.imageFields&fColorMapFollows != 0 { + useLocalColorMap := d.imageFields&fColorMapFollows != 0 + if useLocalColorMap { m.Palette, err = d.readColorMap() if err != nil { return err @@ -180,6 +181,10 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { m.Palette = d.globalColorMap } if d.hasTransparentIndex && int(d.transparentIndex) < len(m.Palette) { + if !useLocalColorMap { + // Clone the global color map. + m.Palette = append(color.Palette(nil), d.globalColorMap...) + } m.Palette[d.transparentIndex] = color.RGBA{} } litWidth, err := d.r.ReadByte() diff --git a/src/image/gif/reader_test.go b/src/image/gif/reader_test.go index fc2041e997..7b6f504367 100644 --- a/src/image/gif/reader_test.go +++ b/src/image/gif/reader_test.go @@ -22,16 +22,16 @@ const ( trailerStr = "\x3b" ) -func TestDecode(t *testing.T) { - // lzwEncode returns an LZW encoding (with 2-bit literals) of n zeroes. - lzwEncode := func(n int) []byte { - b := &bytes.Buffer{} - w := lzw.NewWriter(b, lzw.LSB, 2) - w.Write(make([]byte, n)) - w.Close() - return b.Bytes() - } +// lzwEncode returns an LZW encoding (with 2-bit literals) of n zeroes. +func lzwEncode(n int) []byte { + b := &bytes.Buffer{} + w := lzw.NewWriter(b, lzw.LSB, 2) + w.Write(make([]byte, n)) + w.Close() + return b.Bytes() +} +func TestDecode(t *testing.T) { testCases := []struct { nPix int // The number of pixels in the image data. extra bool // Whether to write an extra block after the LZW-encoded data. @@ -90,6 +90,52 @@ func TestDecode(t *testing.T) { } } +func TestTransparentIndex(t *testing.T) { + b := &bytes.Buffer{} + b.WriteString(headerStr) + b.WriteString(paletteStr) + for transparentIndex := 0; transparentIndex < 3; transparentIndex++ { + if transparentIndex < 2 { + // Write the graphic control for the transparent index. + b.WriteString("\x21\xf9\x00\x01\x00\x00") + b.WriteByte(byte(transparentIndex)) + b.WriteByte(0) + } + // Write an image with bounds 2x1, as per TestDecode. + b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") + enc := lzwEncode(2) + if len(enc) > 0xff { + t.Fatalf("compressed length %d is too large", len(enc)) + } + b.WriteByte(byte(len(enc))) + b.Write(enc) + b.WriteByte(0x00) + } + b.WriteString(trailerStr) + + g, err := DecodeAll(b) + if err != nil { + t.Fatalf("DecodeAll: %v", err) + } + c0 := color.RGBA{paletteStr[0], paletteStr[1], paletteStr[2], 0xff} + c1 := color.RGBA{paletteStr[3], paletteStr[4], paletteStr[5], 0xff} + cz := color.RGBA{} + wants := []color.Palette{ + {cz, c1}, + {c0, cz}, + {c0, c1}, + } + if len(g.Image) != len(wants) { + t.Fatalf("got %d images, want %d", len(g.Image), len(wants)) + } + for i, want := range wants { + got := g.Image[i].Palette + if !reflect.DeepEqual(got, want) { + t.Errorf("palette #%d:\ngot %v\nwant %v", i, got, want) + } + } +} + // testGIF is a simple GIF that we can modify to test different scenarios. var testGIF = []byte{ 'G', 'I', 'F', '8', '9', 'a', -- cgit v1.3 From 93e5cc224e3e5c6dfaad4fc835cc89e33fb957c6 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 22 Sep 2014 09:13:09 -0400 Subject: net/http: replace z_last_test hack with testing.Main LGTM=adg R=rsc, adg CC=golang-codereviews https://golang.org/cl/144240043 --- src/net/http/main_test.go | 109 ++++++++++++++++++++++++++++++++++++++++++++ src/net/http/z_last_test.go | 97 --------------------------------------- 2 files changed, 109 insertions(+), 97 deletions(-) create mode 100644 src/net/http/main_test.go delete mode 100644 src/net/http/z_last_test.go (limited to 'src') diff --git a/src/net/http/main_test.go b/src/net/http/main_test.go new file mode 100644 index 0000000000..9f1dfc3727 --- /dev/null +++ b/src/net/http/main_test.go @@ -0,0 +1,109 @@ +// Copyright 2013 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 http_test + +import ( + "fmt" + "net/http" + "os" + "runtime" + "sort" + "strings" + "testing" + "time" +) + +func TestMain(m *testing.M) { + v := m.Run() + if v == 0 && goroutineLeaked() { + os.Exit(1) + } + os.Exit(v) +} + +func interestingGoroutines() (gs []string) { + buf := make([]byte, 2<<20) + buf = buf[:runtime.Stack(buf, true)] + for _, g := range strings.Split(string(buf), "\n\n") { + sl := strings.SplitN(g, "\n", 2) + if len(sl) != 2 { + continue + } + stack := strings.TrimSpace(sl[1]) + if stack == "" || + strings.Contains(stack, "created by net.startServer") || + strings.Contains(stack, "created by testing.RunTests") || + strings.Contains(stack, "closeWriteAndWait") || + strings.Contains(stack, "testing.Main(") || + // These only show up with GOTRACEBACK=2; Issue 5005 (comment 28) + strings.Contains(stack, "runtime.goexit") || + strings.Contains(stack, "created by runtime.gc") || + strings.Contains(stack, "net/http_test.interestingGoroutines") || + strings.Contains(stack, "runtime.MHeap_Scavenger") { + continue + } + gs = append(gs, stack) + } + sort.Strings(gs) + return +} + +// Verify the other tests didn't leave any goroutines running. +func goroutineLeaked() bool { + if testing.Short() { + // not counting goroutines for leakage in -short mode + return false + } + gs := interestingGoroutines() + + n := 0 + stackCount := make(map[string]int) + for _, g := range gs { + stackCount[g]++ + n++ + } + + if n == 0 { + return false + } + fmt.Fprintf(os.Stderr, "Too many goroutines running after net/http test(s).\n") + for stack, count := range stackCount { + fmt.Fprintf(os.Stderr, "%d instances of:\n%s", count, stack) + } + return true +} + +func afterTest(t *testing.T) { + http.DefaultTransport.(*http.Transport).CloseIdleConnections() + if testing.Short() { + return + } + var bad string + badSubstring := map[string]string{ + ").readLoop(": "a Transport", + ").writeLoop(": "a Transport", + "created by net/http/httptest.(*Server).Start": "an httptest.Server", + "timeoutHandler": "a TimeoutHandler", + "net.(*netFD).connect(": "a timing out dial", + ").noteClientGone(": "a closenotifier sender", + } + var stacks string + for i := 0; i < 4; i++ { + bad = "" + stacks = strings.Join(interestingGoroutines(), "\n\n") + for substr, what := range badSubstring { + if strings.Contains(stacks, substr) { + bad = what + } + } + if bad == "" { + return + } + // Bad stuff found, but goroutines might just still be + // shutting down, so give it some time. + time.Sleep(250 * time.Millisecond) + } + t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks) +} diff --git a/src/net/http/z_last_test.go b/src/net/http/z_last_test.go deleted file mode 100644 index 5a0cc11984..0000000000 --- a/src/net/http/z_last_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2013 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 http_test - -import ( - "net/http" - "runtime" - "sort" - "strings" - "testing" - "time" -) - -func interestingGoroutines() (gs []string) { - buf := make([]byte, 2<<20) - buf = buf[:runtime.Stack(buf, true)] - for _, g := range strings.Split(string(buf), "\n\n") { - sl := strings.SplitN(g, "\n", 2) - if len(sl) != 2 { - continue - } - stack := strings.TrimSpace(sl[1]) - if stack == "" || - strings.Contains(stack, "created by net.startServer") || - strings.Contains(stack, "created by testing.RunTests") || - strings.Contains(stack, "closeWriteAndWait") || - strings.Contains(stack, "testing.Main(") || - // These only show up with GOTRACEBACK=2; Issue 5005 (comment 28) - strings.Contains(stack, "runtime.goexit") || - strings.Contains(stack, "created by runtime.gc") || - strings.Contains(stack, "runtime.MHeap_Scavenger") { - continue - } - gs = append(gs, stack) - } - sort.Strings(gs) - return -} - -// Verify the other tests didn't leave any goroutines running. -// This is in a file named z_last_test.go so it sorts at the end. -func TestGoroutinesRunning(t *testing.T) { - if testing.Short() { - t.Skip("not counting goroutines for leakage in -short mode") - } - gs := interestingGoroutines() - - n := 0 - stackCount := make(map[string]int) - for _, g := range gs { - stackCount[g]++ - n++ - } - - t.Logf("num goroutines = %d", n) - if n > 0 { - t.Error("Too many goroutines.") - for stack, count := range stackCount { - t.Logf("%d instances of:\n%s", count, stack) - } - } -} - -func afterTest(t *testing.T) { - http.DefaultTransport.(*http.Transport).CloseIdleConnections() - if testing.Short() { - return - } - var bad string - badSubstring := map[string]string{ - ").readLoop(": "a Transport", - ").writeLoop(": "a Transport", - "created by net/http/httptest.(*Server).Start": "an httptest.Server", - "timeoutHandler": "a TimeoutHandler", - "net.(*netFD).connect(": "a timing out dial", - ").noteClientGone(": "a closenotifier sender", - } - var stacks string - for i := 0; i < 4; i++ { - bad = "" - stacks = strings.Join(interestingGoroutines(), "\n\n") - for substr, what := range badSubstring { - if strings.Contains(stacks, substr) { - bad = what - } - } - if bad == "" { - return - } - // Bad stuff found, but goroutines might just still be - // shutting down, so give it some time. - time.Sleep(250 * time.Millisecond) - } - t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks) -} -- cgit v1.3 From 5f739d9dcd01730e10d829968755c7b1a7b5f2b7 Mon Sep 17 00:00:00 2001 From: Marko Tiikkaja Date: Mon, 22 Sep 2014 09:19:27 -0400 Subject: database/sql: Close per-tx prepared statements when the associated tx ends LGTM=bradfitz R=golang-codereviews, bradfitz, mattn.jp CC=golang-codereviews https://golang.org/cl/131650043 --- src/database/sql/sql.go | 41 ++++++++++++++++++++++++++++++++++++----- src/database/sql/sql_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go index 90f813d823..731b7a7f79 100644 --- a/src/database/sql/sql.go +++ b/src/database/sql/sql.go @@ -1043,6 +1043,13 @@ type Tx struct { // or Rollback. once done, all operations fail with // ErrTxDone. done bool + + // All Stmts prepared for this transaction. These will be closed after the + // transaction has been committed or rolled back. + stmts struct { + sync.Mutex + v []*Stmt + } } var ErrTxDone = errors.New("sql: Transaction has already been committed or rolled back") @@ -1064,6 +1071,15 @@ func (tx *Tx) grabConn() (*driverConn, error) { return tx.dc, nil } +// Closes all Stmts prepared for this transaction. +func (tx *Tx) closePrepared() { + tx.stmts.Lock() + for _, stmt := range tx.stmts.v { + stmt.Close() + } + tx.stmts.Unlock() +} + // Commit commits the transaction. func (tx *Tx) Commit() error { if tx.done { @@ -1071,8 +1087,12 @@ func (tx *Tx) Commit() error { } defer tx.close() tx.dc.Lock() - defer tx.dc.Unlock() - return tx.txi.Commit() + err := tx.txi.Commit() + tx.dc.Unlock() + if err != driver.ErrBadConn { + tx.closePrepared() + } + return err } // Rollback aborts the transaction. @@ -1082,8 +1102,12 @@ func (tx *Tx) Rollback() error { } defer tx.close() tx.dc.Lock() - defer tx.dc.Unlock() - return tx.txi.Rollback() + err := tx.txi.Rollback() + tx.dc.Unlock() + if err != driver.ErrBadConn { + tx.closePrepared() + } + return err } // Prepare creates a prepared statement for use within a transaction. @@ -1127,6 +1151,9 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) { }, query: query, } + tx.stmts.Lock() + tx.stmts.v = append(tx.stmts.v, stmt) + tx.stmts.Unlock() return stmt, nil } @@ -1155,7 +1182,7 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt { dc.Lock() si, err := dc.ci.Prepare(stmt.query) dc.Unlock() - return &Stmt{ + txs := &Stmt{ db: tx.db, tx: tx, txsi: &driverStmt{ @@ -1165,6 +1192,10 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt { query: stmt.query, stickyErr: err, } + tx.stmts.Lock() + tx.stmts.v = append(tx.stmts.v, txs) + tx.stmts.Unlock() + return txs } // Exec executes a query that doesn't return rows. diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go index 12e5a6fd6f..34efdf254c 100644 --- a/src/database/sql/sql_test.go +++ b/src/database/sql/sql_test.go @@ -441,6 +441,33 @@ func TestExec(t *testing.T) { } } +func TestTxPrepare(t *testing.T) { + db := newTestDB(t, "") + defer closeDB(t, db) + exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") + tx, err := db.Begin() + if err != nil { + t.Fatalf("Begin = %v", err) + } + stmt, err := tx.Prepare("INSERT|t1|name=?,age=?") + if err != nil { + t.Fatalf("Stmt, err = %v, %v", stmt, err) + } + defer stmt.Close() + _, err = stmt.Exec("Bobby", 7) + if err != nil { + t.Fatalf("Exec = %v", err) + } + err = tx.Commit() + if err != nil { + t.Fatalf("Commit = %v", err) + } + // Commit() should have closed the statement + if !stmt.closed { + t.Fatal("Stmt not closed after Commit") + } +} + func TestTxStmt(t *testing.T) { db := newTestDB(t, "") defer closeDB(t, db) @@ -464,6 +491,10 @@ func TestTxStmt(t *testing.T) { if err != nil { t.Fatalf("Commit = %v", err) } + // Commit() should have closed the statement + if !txs.closed { + t.Fatal("Stmt not closed after Commit") + } } // Issue: http://golang.org/issue/2784 -- cgit v1.3 From db56d4d5eb14454e0d4b5c46d0dc89af11fbbf9d Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Mon, 22 Sep 2014 11:46:02 -0700 Subject: text/template: allow comparison functions to work between any integers Previously, signed and unsigned integers could not be compared, but this has problems with things like comparing 'x' with a byte in a string. Since signed and unsigned integers have a well-defined ordering, even though their types are different, and since we already allow comparison regardless of the size of the integers, why not allow it regardless of the sign? Integers only, a fine place to draw the line. Fixes #7489. LGTM=adg R=golang-codereviews, adg CC=golang-codereviews https://golang.org/cl/149780043 --- src/text/template/doc.go | 9 +++-- src/text/template/exec_test.go | 66 +++++++++++++++++++++++--------- src/text/template/funcs.go | 86 +++++++++++++++++++++++++----------------- 3 files changed, 105 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/text/template/doc.go b/src/text/template/doc.go index 7c6efd59cd..223c595c25 100644 --- a/src/text/template/doc.go +++ b/src/text/template/doc.go @@ -338,10 +338,11 @@ arguments will be evaluated.) The comparison functions work on basic types only (or named basic types, such as "type Celsius float32"). They implement the Go rules for comparison of values, except that size and exact type are -ignored, so any integer value may be compared with any other integer -value, any unsigned integer value may be compared with any other -unsigned integer value, and so on. However, as usual, one may not -compare an int with a float32 and so on. +ignored, so any integer value, signed or unsigned, may be compared +with any other integer value. (The arithmetic value is compared, +not the bit pattern, so all negative integers are less than all +unsigned integers.) However, as usual, one may not compare an int +with a float32 and so on. Associated templates diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go index 663aaf3af8..3bffcc1599 100644 --- a/src/text/template/exec_test.go +++ b/src/text/template/exec_test.go @@ -902,8 +902,8 @@ var cmpTests = []cmpTest{ {"eq 1 2", "false", true}, {"eq `xy` `xy`", "true", true}, {"eq `xy` `xyz`", "false", true}, - {"eq .Xuint .Xuint", "true", true}, - {"eq .Xuint .Yuint", "false", true}, + {"eq .Uthree .Uthree", "true", true}, + {"eq .Uthree .Ufour", "false", true}, {"eq 3 4 5 6 3", "true", true}, {"eq 3 4 5 6 7", "false", true}, {"ne true true", "false", true}, @@ -916,16 +916,16 @@ var cmpTests = []cmpTest{ {"ne 1 2", "true", true}, {"ne `xy` `xy`", "false", true}, {"ne `xy` `xyz`", "true", true}, - {"ne .Xuint .Xuint", "false", true}, - {"ne .Xuint .Yuint", "true", true}, + {"ne .Uthree .Uthree", "false", true}, + {"ne .Uthree .Ufour", "true", true}, {"lt 1.5 1.5", "false", true}, {"lt 1.5 2.5", "true", true}, {"lt 1 1", "false", true}, {"lt 1 2", "true", true}, {"lt `xy` `xy`", "false", true}, {"lt `xy` `xyz`", "true", true}, - {"lt .Xuint .Xuint", "false", true}, - {"lt .Xuint .Yuint", "true", true}, + {"lt .Uthree .Uthree", "false", true}, + {"lt .Uthree .Ufour", "true", true}, {"le 1.5 1.5", "true", true}, {"le 1.5 2.5", "true", true}, {"le 2.5 1.5", "false", true}, @@ -935,9 +935,9 @@ var cmpTests = []cmpTest{ {"le `xy` `xy`", "true", true}, {"le `xy` `xyz`", "true", true}, {"le `xyz` `xy`", "false", true}, - {"le .Xuint .Xuint", "true", true}, - {"le .Xuint .Yuint", "true", true}, - {"le .Yuint .Xuint", "false", true}, + {"le .Uthree .Uthree", "true", true}, + {"le .Uthree .Ufour", "true", true}, + {"le .Ufour .Uthree", "false", true}, {"gt 1.5 1.5", "false", true}, {"gt 1.5 2.5", "false", true}, {"gt 1 1", "false", true}, @@ -945,9 +945,9 @@ var cmpTests = []cmpTest{ {"gt 1 2", "false", true}, {"gt `xy` `xy`", "false", true}, {"gt `xy` `xyz`", "false", true}, - {"gt .Xuint .Xuint", "false", true}, - {"gt .Xuint .Yuint", "false", true}, - {"gt .Yuint .Xuint", "true", true}, + {"gt .Uthree .Uthree", "false", true}, + {"gt .Uthree .Ufour", "false", true}, + {"gt .Ufour .Uthree", "true", true}, {"ge 1.5 1.5", "true", true}, {"ge 1.5 2.5", "false", true}, {"ge 2.5 1.5", "true", true}, @@ -957,11 +957,40 @@ var cmpTests = []cmpTest{ {"ge `xy` `xy`", "true", true}, {"ge `xy` `xyz`", "false", true}, {"ge `xyz` `xy`", "true", true}, - {"ge .Xuint .Xuint", "true", true}, - {"ge .Xuint .Yuint", "false", true}, - {"ge .Yuint .Xuint", "true", true}, + {"ge .Uthree .Uthree", "true", true}, + {"ge .Uthree .Ufour", "false", true}, + {"ge .Ufour .Uthree", "true", true}, + // Mixing signed and unsigned integers. + {"eq .Uthree .Three", "true", true}, + {"eq .Three .Uthree", "true", true}, + {"le .Uthree .Three", "true", true}, + {"le .Three .Uthree", "true", true}, + {"ge .Uthree .Three", "true", true}, + {"ge .Three .Uthree", "true", true}, + {"lt .Uthree .Three", "false", true}, + {"lt .Three .Uthree", "false", true}, + {"gt .Uthree .Three", "false", true}, + {"gt .Three .Uthree", "false", true}, + {"eq .Ufour .Three", "false", true}, + {"lt .Ufour .Three", "false", true}, + {"gt .Ufour .Three", "true", true}, + {"eq .NegOne .Uthree", "false", true}, + {"eq .Uthree .NegOne", "false", true}, + {"ne .NegOne .Uthree", "true", true}, + {"ne .Uthree .NegOne", "true", true}, + {"lt .NegOne .Uthree", "true", true}, + {"lt .Uthree .NegOne", "false", true}, + {"le .NegOne .Uthree", "true", true}, + {"le .Uthree .NegOne", "false", true}, + {"gt .NegOne .Uthree", "false", true}, + {"gt .Uthree .NegOne", "true", true}, + {"ge .NegOne .Uthree", "false", true}, + {"ge .Uthree .NegOne", "true", true}, + {"eq (index `x` 0) 'x'", "true", true}, // The example that triggered this rule. + {"eq (index `x` 0) 'y'", "false", true}, // Errors {"eq `xy` 1", "", false}, // Different types. + {"eq 2 2.0", "", false}, // Different types. {"lt true true", "", false}, // Unordered types. {"lt 1+0i 1+0i", "", false}, // Unordered types. } @@ -969,13 +998,14 @@ var cmpTests = []cmpTest{ func TestComparison(t *testing.T) { b := new(bytes.Buffer) var cmpStruct = struct { - Xuint, Yuint uint - }{3, 4} + Uthree, Ufour uint + NegOne, Three int + }{3, 4, -1, 3} for _, test := range cmpTests { text := fmt.Sprintf("{{if %s}}true{{else}}false{{end}}", test.expr) tmpl, err := New("empty").Parse(text) if err != nil { - t.Fatal(err) + t.Fatalf("%q: %s", test.expr, err) } b.Reset() err = tmpl.Execute(b, &cmpStruct) diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go index e854122624..39ee5ed68f 100644 --- a/src/text/template/funcs.go +++ b/src/text/template/funcs.go @@ -314,25 +314,34 @@ func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { if err != nil { return false, err } - if k1 != k2 { - return false, errBadComparison - } truth := false - switch k1 { - case boolKind: - truth = v1.Bool() == v2.Bool() - case complexKind: - truth = v1.Complex() == v2.Complex() - case floatKind: - truth = v1.Float() == v2.Float() - case intKind: - truth = v1.Int() == v2.Int() - case stringKind: - truth = v1.String() == v2.String() - case uintKind: - truth = v1.Uint() == v2.Uint() - default: - panic("invalid kind") + if k1 != k2 { + // Special case: Can compare integer values regardless of type's sign. + switch { + case k1 == intKind && k2 == uintKind: + truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint() + case k1 == uintKind && k2 == intKind: + truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int()) + default: + return false, errBadComparison + } + } else { + switch k1 { + case boolKind: + truth = v1.Bool() == v2.Bool() + case complexKind: + truth = v1.Complex() == v2.Complex() + case floatKind: + truth = v1.Float() == v2.Float() + case intKind: + truth = v1.Int() == v2.Int() + case stringKind: + truth = v1.String() == v2.String() + case uintKind: + truth = v1.Uint() == v2.Uint() + default: + panic("invalid kind") + } } if truth { return true, nil @@ -360,23 +369,32 @@ func lt(arg1, arg2 interface{}) (bool, error) { if err != nil { return false, err } - if k1 != k2 { - return false, errBadComparison - } truth := false - switch k1 { - case boolKind, complexKind: - return false, errBadComparisonType - case floatKind: - truth = v1.Float() < v2.Float() - case intKind: - truth = v1.Int() < v2.Int() - case stringKind: - truth = v1.String() < v2.String() - case uintKind: - truth = v1.Uint() < v2.Uint() - default: - panic("invalid kind") + if k1 != k2 { + // Special case: Can compare integer values regardless of type's sign. + switch { + case k1 == intKind && k2 == uintKind: + truth = v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() + case k1 == uintKind && k2 == intKind: + truth = v2.Int() >= 0 && v1.Uint() < uint64(v2.Int()) + default: + return false, errBadComparison + } + } else { + switch k1 { + case boolKind, complexKind: + return false, errBadComparisonType + case floatKind: + truth = v1.Float() < v2.Float() + case intKind: + truth = v1.Int() < v2.Int() + case stringKind: + truth = v1.String() < v2.String() + case uintKind: + truth = v1.Uint() < v2.Uint() + default: + panic("invalid kind") + } } return truth, nil } -- cgit v1.3 From 78b5321e823d6dbb7a009ec73775f7b5e8dc95e7 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Mon, 22 Sep 2014 11:58:15 -0700 Subject: fmt: make printing of ints 25-35% faster Inspired by a remark by Leonard Holz, use constants for division BenchmarkSprintfEmpty 130 132 +1.54% BenchmarkSprintfString 438 437 -0.23% BenchmarkSprintfInt 417 414 -0.72% BenchmarkSprintfIntInt 663 691 +4.22% BenchmarkSprintfPrefixedInt 791 774 -2.15% BenchmarkSprintfFloat 701 686 -2.14% BenchmarkManyArgs 2584 2469 -4.45% BenchmarkFprintInt 488 357 -26.84% BenchmarkFprintIntNoAlloc 402 265 -34.08% BenchmarkScanInts 1244346 1267574 +1.87% BenchmarkScanRecursiveInt 1748741 1724138 -1.41% Update #3463 LGTM=josharian, rsc R=golang-codereviews, josharian, rsc CC=golang-codereviews https://golang.org/cl/144250043 --- src/fmt/fmt_test.go | 17 +++++++++++++++++ src/fmt/format.go | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go index a212c9f702..cca0a495ff 100644 --- a/src/fmt/fmt_test.go +++ b/src/fmt/fmt_test.go @@ -854,6 +854,23 @@ func BenchmarkManyArgs(b *testing.B) { }) } +func BenchmarkFprintInt(b *testing.B) { + var buf bytes.Buffer + for i := 0; i < b.N; i++ { + buf.Reset() + Fprint(&buf, 123456) + } +} + +func BenchmarkFprintIntNoAlloc(b *testing.B) { + var x interface{} = 123456 + var buf bytes.Buffer + for i := 0; i < b.N; i++ { + buf.Reset() + Fprint(&buf, x) + } +} + var mallocBuf bytes.Buffer var mallocPointer *int // A pointer so we know the interface value won't allocate. diff --git a/src/fmt/format.go b/src/fmt/format.go index 8aeffd7b2b..255167c8f5 100644 --- a/src/fmt/format.go +++ b/src/fmt/format.go @@ -199,10 +199,36 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) { // block but it's not worth the duplication, so ua has 64 bits. i := len(buf) ua := uint64(a) - for ua >= base { - i-- - buf[i] = digits[ua%base] - ua /= base + // use constants for the division and modulo for more efficient code. + // switch cases ordered by popularity. + switch base { + case 10: + for ua >= 10 { + i-- + next := ua / 10 + buf[i] = byte('0' + ua - next*10) + ua = next + } + case 16: + for ua >= 16 { + i-- + buf[i] = digits[ua&0xF] + ua >>= 4 + } + case 8: + for ua >= 8 { + i-- + buf[i] = byte('0' + ua&7) + ua >>= 3 + } + case 2: + for ua >= 2 { + i-- + buf[i] = byte('0' + ua&1) + ua >>= 1 + } + default: + panic("fmt: unknown base; can't happen") } i-- buf[i] = digits[ua] -- cgit v1.3 From 892b5074f521ae812b880808e0bd79ee2a02b1a1 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Mon, 22 Sep 2014 15:35:25 -0700 Subject: fmt: document that self-recursive data structures can be fatal Fixes #8241. LGTM=iant R=golang-codereviews, iant CC=golang-codereviews https://golang.org/cl/144420043 --- src/fmt/doc.go | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/fmt/doc.go b/src/fmt/doc.go index 5af8d3e717..b7eaedc11e 100644 --- a/src/fmt/doc.go +++ b/src/fmt/doc.go @@ -147,6 +147,10 @@ func (x X) String() string { return Sprintf("<%s>", x) } convert the value before recurring: func (x X) String() string { return Sprintf("<%s>", string(x)) } + Infinite recursion can also be triggered by self-referential data + structures, such as a slice that contains itself as an element, if + that type has a String method. Such pathologies are rare, however, + and the package does not protect against them. Explicit argument indexes: -- cgit v1.3 From 5d5e73b14a46178a636b20f45ed8f8fae0177dee Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Mon, 22 Sep 2014 17:48:13 -0700 Subject: text/template: type-check chained node as argument Was just a missing case (literally) in the type checker. Fixes #8473. LGTM=adg R=golang-codereviews, adg CC=golang-codereviews https://golang.org/cl/142460043 --- src/text/template/exec.go | 2 ++ src/text/template/exec_test.go | 8 ++++++++ 2 files changed, 10 insertions(+) (limited to 'src') diff --git a/src/text/template/exec.go b/src/text/template/exec.go index 8e155d478e..f6eed662b7 100644 --- a/src/text/template/exec.go +++ b/src/text/template/exec.go @@ -636,6 +636,8 @@ func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) refle return s.validateType(s.evalPipeline(dot, arg), typ) case *parse.IdentifierNode: return s.evalFunction(dot, arg, arg, nil, zero) + case *parse.ChainNode: + return s.validateType(s.evalChainNode(dot, arg, nil, zero), typ) } switch typ.Kind() { case reflect.Bool: diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go index 3bffcc1599..e2cf2d3705 100644 --- a/src/text/template/exec_test.go +++ b/src/text/template/exec_test.go @@ -176,6 +176,12 @@ func (t *T) Method3(v interface{}) string { return fmt.Sprintf("Method3: %v", v) } +func (t *T) Copy() *T { + n := new(T) + *n = *t + return n +} + func (t *T) MAdd(a int, b []int) []int { v := make([]int, len(b)) for i, x := range b { @@ -519,6 +525,8 @@ var execTests = []execTest{ {"bug12xE", "{{printf `%T` 0xEE}}", "int", T{}, true}, {"bug12Xe", "{{printf `%T` 0Xef}}", "int", T{}, true}, {"bug12XE", "{{printf `%T` 0XEE}}", "int", T{}, true}, + // Chained nodes did not work as arguments. Issue 8473. + {"bug13", "{{print (.Copy).I}}", "17", tVal, true}, } func zeroArgs() string { -- cgit v1.3 From dcb594ec30f428b4b116682a7536ed694526f0e5 Mon Sep 17 00:00:00 2001 From: Dmitriy Vyukov Date: Mon, 22 Sep 2014 19:51:53 -0700 Subject: runtime: remove unused function declaration LGTM=bradfitz R=golang-codereviews, bradfitz CC=golang-codereviews https://golang.org/cl/145970043 --- src/runtime/mgc0.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c index 03ca288496..54728d5ada 100644 --- a/src/runtime/mgc0.c +++ b/src/runtime/mgc0.c @@ -1263,7 +1263,6 @@ struct gc_args }; static void gc(struct gc_args *args); -static void mgc(G *gp); int32 runtime·readgogc(void) -- cgit v1.3 From c486d4130d01f1034901d65c764b8e8ae329a01b Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Tue, 23 Sep 2014 15:34:38 +1000 Subject: runtime: fix runtime.Breakpoint on ARMv5 Fixes #8775. Use the illegal instruction suggested by Ian in https://golang.org/cl/144180043/#msg4 on all arm arches. LGTM=minux R=golang-codereviews, gobot, rsc CC=golang-codereviews, iant, minux https://golang.org/cl/146130043 --- src/runtime/asm_arm.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s index f67f94939b..38d97b78f3 100644 --- a/src/runtime/asm_arm.s +++ b/src/runtime/asm_arm.s @@ -96,7 +96,7 @@ TEXT runtime·breakpoint(SB),NOSPLIT,$0-0 #ifdef GOOS_nacl WORD $0xe125be7f // BKPT 0x5bef, NACL_INSTR_ARM_BREAKPOINT #else - WORD $0xe1200071 // BKPT 0x0001 + WORD $0xe7f001f0 // undefined instruction that gdb understands is a software breakpoint #endif RET -- cgit v1.3 From 82ddcc05f44a510118a1fc8bb8e3552a92d8f441 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 23 Sep 2014 14:26:20 -0700 Subject: os: fix another case where RemoveAll should return nil This hopefully fixes issue 8793. Fixes #8793 LGTM=iant R=rsc, iant CC=golang-codereviews https://golang.org/cl/150860046 --- src/os/path.go | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/os/path.go b/src/os/path.go index 6cc69403b6..b1a90b3e52 100644 --- a/src/os/path.go +++ b/src/os/path.go @@ -86,6 +86,9 @@ func RemoveAll(path string) error { // Directory. fd, err := Open(path) if err != nil { + if IsNotExist(err) { + return nil + } return err } -- cgit v1.3 From db492b8df41cd90ebecaf69a73bf4cc5e0db5f20 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 23 Sep 2014 14:55:19 -0700 Subject: os: add a comment inside RemoveAll LGTM=r R=r CC=golang-codereviews https://golang.org/cl/149950043 --- src/os/path.go | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/os/path.go b/src/os/path.go index b1a90b3e52..24a3415b46 100644 --- a/src/os/path.go +++ b/src/os/path.go @@ -87,6 +87,8 @@ func RemoveAll(path string) error { fd, err := Open(path) if err != nil { if IsNotExist(err) { + // Race. It was deleted between the Lstat and Open. + // Return nil per RemoveAll's docs. return nil } return err -- cgit v1.3 From 1193993c1db83ee8c0a8e86e6d41db1dd1982002 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Tue, 23 Sep 2014 18:24:35 -0700 Subject: cmd/pack: fix c command for existing file There were at least two bugs: 1) It would overwrite a non-archive. 2) It would truncate a non-archive and then fail. In general the file handling was too clever to be correct. Make it more straightforward, doing the creation separately from archive management. Fixes #8369. LGTM=adg, iant R=golang-codereviews, adg, iant CC=golang-codereviews https://golang.org/cl/147010046 --- src/cmd/pack/doc.go | 4 ++++ src/cmd/pack/pack.go | 50 +++++++++++++++++++++++++++++++++-------------- src/cmd/pack/pack_test.go | 23 +++++++++++++++++----- 3 files changed, 57 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/cmd/pack/doc.go b/src/cmd/pack/doc.go index 1529e07e90..a702594e23 100644 --- a/src/cmd/pack/doc.go +++ b/src/cmd/pack/doc.go @@ -20,6 +20,10 @@ The operation op is given by one of these letters: t list files from the archive x extract files from the archive +The archive argument to the c command must be non-existent or a +valid archive file, which will be cleared before adding new entries. It +is an error if the file exists but is not an archive. + For the p, t, and x commands, listing no names on the command line causes the operation to apply to all files in the archive. diff --git a/src/cmd/pack/pack.go b/src/cmd/pack/pack.go index 594433712d..ffb2d617ae 100644 --- a/src/cmd/pack/pack.go +++ b/src/cmd/pack/pack.go @@ -142,16 +142,19 @@ type Archive struct { matchAll bool // match all files in archive } -// archive opens (or if necessary creates) the named archive. +// archive opens (and if necessary creates) the named archive. func archive(name string, mode int, files []string) *Archive { - fd, err := os.OpenFile(name, mode, 0) - if err != nil && mode&^os.O_TRUNC == os.O_RDWR && os.IsNotExist(err) { - fd, err = create(name) + // If the file exists, it must be an archive. If it doesn't exist, or if + // we're doing the c command, indicated by O_TRUNC, truncate the archive. + if !existingArchive(name) || mode&os.O_TRUNC != 0 { + create(name) + mode &^= os.O_TRUNC } + fd, err := os.OpenFile(name, mode, 0) if err != nil { log.Fatal(err) } - mustBeArchive(fd) + checkHeader(fd) return &Archive{ fd: fd, files: files, @@ -160,23 +163,40 @@ func archive(name string, mode int, files []string) *Archive { } // create creates and initializes an archive that does not exist. -func create(name string) (*os.File, error) { - fd, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) +func create(name string) { + fd, err := os.Create(name) if err != nil { - return nil, err + log.Fatal(err) + } + _, err = fmt.Fprint(fd, arHeader) + if err != nil { + log.Fatal(err) + } + fd.Close() +} + +// existingArchive reports whether the file exists and is a valid archive. +// If it exists but is not an archive, existingArchive will exit. +func existingArchive(name string) bool { + fd, err := os.Open(name) + if err != nil { + if os.IsNotExist(err) { + return false + } + log.Fatal("cannot open file: %s", err) } - fmt.Fprint(fd, arHeader) - fd.Seek(0, 0) - return fd, nil + checkHeader(fd) + fd.Close() + return true } -// mustBeArchive verifies the header of the file. It assumes the file offset -// is 0 coming in, and leaves it positioned immediately after the header. -func mustBeArchive(fd *os.File) { +// checkHeader verifies the header of the file. It assumes the file +// is positioned at 0 and leaves it positioned at the end of the header. +func checkHeader(fd *os.File) { buf := make([]byte, len(arHeader)) _, err := io.ReadFull(fd, buf) if err != nil || string(buf) != arHeader { - log.Fatal("file is not an archive: bad header") + log.Fatal("%s is not an archive: bad header", fd.Name()) } } diff --git a/src/cmd/pack/pack_test.go b/src/cmd/pack/pack_test.go index e41cf3ce42..cf6121fcc1 100644 --- a/src/cmd/pack/pack_test.go +++ b/src/cmd/pack/pack_test.go @@ -56,11 +56,8 @@ func tmpDir(t *testing.T) string { return name } -// Test that we can create an archive, write to it, and get the same contents back. -// Tests the rv and then the pv command on a new archive. -func TestCreate(t *testing.T) { - dir := tmpDir(t) - defer os.RemoveAll(dir) +// testCreate creates an archive in the specified directory. +func testCreate(t *testing.T, dir string) { name := filepath.Join(dir, "pack.a") ar := archive(name, os.O_RDWR, nil) // Add an entry by hand. @@ -85,6 +82,22 @@ func TestCreate(t *testing.T) { } } +// Test that we can create an archive, write to it, and get the same contents back. +// Tests the rv and then the pv command on a new archive. +func TestCreate(t *testing.T) { + dir := tmpDir(t) + defer os.RemoveAll(dir) + testCreate(t, dir) +} + +// Test that we can create an archive twice with the same name (Issue 8369). +func TestCreateTwice(t *testing.T) { + dir := tmpDir(t) + defer os.RemoveAll(dir) + testCreate(t, dir) + testCreate(t, dir) +} + // Test that we can create an archive, put some files in it, and get back a correct listing. // Tests the tv command. func TestTableOfContents(t *testing.T) { -- cgit v1.3 From 7283e08cbf06bcd32a391183e26080cff301e7f9 Mon Sep 17 00:00:00 2001 From: Hector Martin Cantero Date: Wed, 24 Sep 2014 13:20:25 -0400 Subject: runtime: keep g->syscallsp consistent after cgo->Go callbacks Normally, the caller to runtime.entersyscall() must not return before calling runtime.exitsyscall(), lest g->syscallsp become a dangling pointer. runtime.cgocallbackg() violates this constraint. To work around this, save g->syscallsp and g->syscallpc around cgo->Go callbacks, then restore them after calling runtime.entersyscall(), which restores the syscall stack frame pointer saved by cgocall. This allows the GC to correctly trace a goroutine that is currently returning from a Go->cgo->Go chain. This also adds a check to proc.c that panics if g->syscallsp is clearly invalid. It is not 100% foolproof, as it will not catch a case where the stack was popped then pushed back beyond g->syscallsp, but it does catch the present cgo issue and makes existing tests fail without the bugfix. Fixes #7978. LGTM=dvyukov, rsc R=golang-codereviews, dvyukov, minux, bradfitz, iant, gobot, rsc CC=golang-codereviews, rsc https://golang.org/cl/131910043 --- misc/cgo/test/cgo_test.go | 1 + misc/cgo/test/issue7978.go | 99 ++++++++++++++++++++++++++++++++++++++++++++++ src/run.bash | 2 + src/run.bat | 7 ++++ src/runtime/cgocall.go | 12 +++++- src/runtime/proc.c | 39 ++++++++++++------ src/runtime/runtime.h | 1 + src/runtime/stubs.go | 1 + 8 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 misc/cgo/test/issue7978.go (limited to 'src') diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go index 3783af061c..1899d46053 100644 --- a/misc/cgo/test/cgo_test.go +++ b/misc/cgo/test/cgo_test.go @@ -56,5 +56,6 @@ func TestNaming(t *testing.T) { testNaming(t) } func Test7560(t *testing.T) { test7560(t) } func Test5242(t *testing.T) { test5242(t) } func Test8092(t *testing.T) { test8092(t) } +func Test7978(t *testing.T) { test7978(t) } func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } diff --git a/misc/cgo/test/issue7978.go b/misc/cgo/test/issue7978.go new file mode 100644 index 0000000000..39864476ce --- /dev/null +++ b/misc/cgo/test/issue7978.go @@ -0,0 +1,99 @@ +// Copyright 2014 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 7978. Stack tracing didn't work during cgo code after calling a Go +// callback. Make sure GC works and the stack trace is correct. + +package cgotest + +/* +#include + +void issue7978cb(void); + +// use ugly atomic variable sync since that doesn't require calling back into +// Go code or OS dependencies +static void issue7978c(uint32_t *sync) { + while(__sync_fetch_and_add(sync, 0) != 0) + ; + __sync_fetch_and_add(sync, 1); + while(__sync_fetch_and_add(sync, 0) != 2) + ; + issue7978cb(); + __sync_fetch_and_add(sync, 1); + while(__sync_fetch_and_add(sync, 0) != 6) + ; +} +*/ +import "C" + +import ( + "runtime" + "strings" + "sync/atomic" + "testing" +) + +var issue7978sync uint32 + +func issue7978check(t *testing.T, wantFunc string, badFunc string, depth int) { + runtime.GC() + buf := make([]byte, 65536) + trace := string(buf[:runtime.Stack(buf, true)]) + for _, goroutine := range strings.Split(trace, "\n\n") { + if strings.Contains(goroutine, "test.issue7978go") { + trace := strings.Split(goroutine, "\n") + // look for the expected function in the stack + for i := 0; i < depth; i++ { + if badFunc != "" && strings.Contains(trace[1+2*i], badFunc) { + t.Errorf("bad stack: found %s in the stack:\n%s", badFunc, goroutine) + return + } + if strings.Contains(trace[1+2*i], wantFunc) { + return + } + } + t.Errorf("bad stack: didn't find %s in the stack:\n%s", wantFunc, goroutine) + return + } + } + t.Errorf("bad stack: goroutine not found. Full stack dump:\n%s", trace) +} + +func issue7978wait(store uint32, wait uint32) { + if store != 0 { + atomic.StoreUint32(&issue7978sync, store) + } + for atomic.LoadUint32(&issue7978sync) != wait { + runtime.Gosched() + } +} + +//export issue7978cb +func issue7978cb() { + issue7978wait(3, 4) +} + +func issue7978go() { + C.issue7978c((*C.uint32_t)(&issue7978sync)) + issue7978wait(7, 8) +} + +func test7978(t *testing.T) { + issue7978sync = 0 + go issue7978go() + // test in c code, before callback + issue7978wait(0, 1) + issue7978check(t, "runtime.cgocall_errno(", "", 1) + // test in go code, during callback + issue7978wait(2, 3) + issue7978check(t, "test.issue7978cb(", "test.issue7978go", 3) + // test in c code, after callback + issue7978wait(4, 5) + issue7978check(t, "runtime.cgocall_errno(", "runtime.cgocallback", 1) + // test in go code, after return from cgo + issue7978wait(6, 7) + issue7978check(t, "test.issue7978go(", "", 3) + atomic.StoreUint32(&issue7978sync, 8) +} diff --git a/src/run.bash b/src/run.bash index b5f061d885..d6e53304d8 100755 --- a/src/run.bash +++ b/src/run.bash @@ -119,6 +119,8 @@ go run $GOROOT/test/run.go - . || exit 1 [ "$CGO_ENABLED" != 1 ] || (xcd ../misc/cgo/test +# cgo tests inspect the traceback for runtime functions +export GOTRACEBACK=2 go test -ldflags '-linkmode=auto' || exit 1 # linkmode=internal fails on dragonfly since errno is a TLS relocation. [ "$GOHOSTOS" == dragonfly ] || go test -ldflags '-linkmode=internal' || exit 1 diff --git a/src/run.bat b/src/run.bat index 62692acaf2..309e06d507 100644 --- a/src/run.bat +++ b/src/run.bat @@ -90,11 +90,18 @@ go run "%GOROOT%\test\run.go" - ..\misc\cgo\stdio if errorlevel 1 goto fail echo. +# cgo tests inspect the traceback for runtime functions +set OLDGOTRACEBACK=%GOTRACEBACK% +set GOTRACEBACK=2 + echo # ..\misc\cgo\test go test ..\misc\cgo\test if errorlevel 1 goto fail echo. +set GOTRACEBACK=%OLDGOTRACEBACK% +set OLDGOTRACEBACK= + echo # ..\misc\cgo\testso cd ..\misc\cgo\testso set FAIL=0 diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index a21474b01f..7fd91469eb 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -177,14 +177,22 @@ func cfree(p unsafe.Pointer) { // Call from C back to Go. //go:nosplit func cgocallbackg() { - if gp := getg(); gp != gp.m.curg { + gp := getg() + if gp != gp.m.curg { println("runtime: bad g in cgocallback") exit(2) } + // entersyscall saves the caller's SP to allow the GC to trace the Go + // stack. However, since we're returning to an earlier stack frame and + // need to pair with the entersyscall() call made by cgocall, we must + // save syscall* and let reentersyscall restore them. + savedsp := unsafe.Pointer(gp.syscallsp) + savedpc := gp.syscallpc exitsyscall() // coming out of cgo call cgocallbackg1() - entersyscall() // going back to cgo call + // going back to cgo call + reentersyscall(savedpc, savedsp) } func cgocallbackg1() { diff --git a/src/runtime/proc.c b/src/runtime/proc.c index 3f4179d473..564798be7b 100644 --- a/src/runtime/proc.c +++ b/src/runtime/proc.c @@ -1700,9 +1700,9 @@ goexit0(G *gp) #pragma textflag NOSPLIT static void -save(void *pc, uintptr sp) +save(uintptr pc, uintptr sp) { - g->sched.pc = (uintptr)pc; + g->sched.pc = pc; g->sched.sp = sp; g->sched.lr = 0; g->sched.ret = 0; @@ -1730,9 +1730,15 @@ static void entersyscall_gcwait(void); // In practice, this means that we make the fast path run through // entersyscall doing no-split things, and the slow path has to use onM // to run bigger things on the m stack. +// +// reentersyscall is the entry point used by cgo callbacks, where explicitly +// saved SP and PC are restored. This is needed when exitsyscall will be called +// from a function further up in the call stack than the parent, as g->syscallsp +// must always point to a valid stack frame. entersyscall below is the normal +// entry point for syscalls, which obtains the SP and PC from the caller. #pragma textflag NOSPLIT void -·entersyscall(int32 dummy) +runtime·reentersyscall(uintptr pc, uintptr sp) { void (*fn)(void); @@ -1748,9 +1754,9 @@ void g->throwsplit = 1; // Leave SP around for GC and traceback. - save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); - g->syscallsp = g->sched.sp; - g->syscallpc = g->sched.pc; + save(pc, sp); + g->syscallsp = sp; + g->syscallpc = pc; runtime·casgstatus(g, Grunning, Gsyscall); if(g->syscallsp < g->stack.lo || g->stack.hi < g->syscallsp) { fn = entersyscall_bad; @@ -1760,7 +1766,7 @@ void if(runtime·atomicload(&runtime·sched.sysmonwait)) { // TODO: fast atomic fn = entersyscall_sysmon; runtime·onM(&fn); - save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); + save(pc, sp); } g->m->mcache = nil; @@ -1769,7 +1775,7 @@ void if(runtime·sched.gcwaiting) { fn = entersyscall_gcwait; runtime·onM(&fn); - save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); + save(pc, sp); } // Goroutines must not split stacks in Gsyscall status (it would corrupt g->sched). @@ -1779,6 +1785,14 @@ void g->m->locks--; } +// Standard syscall entry used by the go syscall library and normal cgo calls. +#pragma textflag NOSPLIT +void +·entersyscall(int32 dummy) +{ + runtime·reentersyscall((uintptr)runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); +} + static void entersyscall_bad(void) { @@ -1826,7 +1840,7 @@ void g->stackguard0 = StackPreempt; // see comment in entersyscall // Leave SP around for GC and traceback. - save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); + save((uintptr)runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); g->syscallsp = g->sched.sp; g->syscallpc = g->sched.pc; runtime·casgstatus(g, Grunning, Gsyscall); @@ -1839,7 +1853,7 @@ void runtime·onM(&fn); // Resave for traceback during blocked call. - save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); + save((uintptr)runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy)); g->m->locks--; } @@ -1856,12 +1870,15 @@ entersyscallblock_handoff(void) // from the low-level system calls used by the runtime. #pragma textflag NOSPLIT void -runtime·exitsyscall(void) +·exitsyscall(int32 dummy) { void (*fn)(G*); g->m->locks++; // see comment in entersyscall + if(runtime·getcallersp(&dummy) > g->syscallsp) + runtime·throw("exitsyscall: syscall frame is no longer valid"); + g->waitsince = 0; if(exitsyscallfast()) { // There's a cpu for us, so we can run. diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 7fefbc2997..3a6d3e3262 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -901,6 +901,7 @@ void runtime·goexit(void); void runtime·asmcgocall(void (*fn)(void*), void*); int32 runtime·asmcgocall_errno(void (*fn)(void*), void*); void runtime·entersyscall(void); +void runtime·reentersyscall(uintptr, uintptr); void runtime·entersyscallblock(void); void runtime·exitsyscall(void); G* runtime·newproc1(FuncVal*, byte*, int32, int32, void*); diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go index 2e6aadca7a..1381c7efdb 100644 --- a/src/runtime/stubs.go +++ b/src/runtime/stubs.go @@ -164,6 +164,7 @@ func noescape(p unsafe.Pointer) unsafe.Pointer { } func entersyscall() +func reentersyscall(pc uintptr, sp unsafe.Pointer) func entersyscallblock() func exitsyscall() -- cgit v1.3 From 6f219e8b847c731a6c0d3695b74f312a1ea705a5 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 14:18:25 -0400 Subject: runtime: fix LastGC comment I have no idea what "absolute time" means. LGTM=dvyukov, r R=r, dvyukov CC=golang-codereviews https://golang.org/cl/144320043 --- src/runtime/mem.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/runtime/mem.go b/src/runtime/mem.go index b3c216f18e..438f22ec09 100644 --- a/src/runtime/mem.go +++ b/src/runtime/mem.go @@ -41,8 +41,8 @@ type MemStats struct { OtherSys uint64 // other system allocations // Garbage collector statistics. - NextGC uint64 // next run in HeapAlloc time (bytes) - LastGC uint64 // last run in absolute time (ns) + NextGC uint64 // next collection will happen when HeapAlloc ≥ this amount + LastGC uint64 // end time of last collection (nanoseconds since 1970) PauseTotalNs uint64 PauseNs [256]uint64 // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256] NumGC uint32 -- cgit v1.3 From a0785a53add4253db84349d58abbe2ba8be130d9 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 15:10:38 -0400 Subject: cmd/go: prohibit C sources files unless using cgo Those C files would have been compiled with 6c. It's close to impossible to use C correctly anymore, and the C compilers are going away eventually. Make them unavailable now. go1.4.txt change in CL 145890046 LGTM=iant R=iant CC=golang-codereviews, r https://golang.org/cl/149720043 --- misc/cgo/test/backdoor/backdoor.go | 3 +-- misc/cgo/test/backdoor/backdoor_gccgo.go | 11 ----------- misc/cgo/test/backdoor/runtime.c | 18 ------------------ misc/cgo/test/issue7695_test.go | 30 ------------------------------ src/cmd/go/pkg.go | 10 ++++++++++ src/cmd/go/test.bash | 14 ++++++++++++++ src/cmd/go/testdata/src/badc/x.c | 1 + src/cmd/go/testdata/src/badc/x.go | 1 + src/net/empty.c | 8 -------- src/net/empty.s | 8 ++++++++ src/runtime/debug/debug.c | 9 --------- src/runtime/debug/debug.s | 9 +++++++++ 12 files changed, 44 insertions(+), 78 deletions(-) delete mode 100644 misc/cgo/test/backdoor/backdoor_gccgo.go delete mode 100644 misc/cgo/test/backdoor/runtime.c delete mode 100644 misc/cgo/test/issue7695_test.go create mode 100644 src/cmd/go/testdata/src/badc/x.c create mode 100644 src/cmd/go/testdata/src/badc/x.go delete mode 100644 src/net/empty.c create mode 100644 src/net/empty.s delete mode 100644 src/runtime/debug/debug.c create mode 100644 src/runtime/debug/debug.s (limited to 'src') diff --git a/misc/cgo/test/backdoor/backdoor.go b/misc/cgo/test/backdoor/backdoor.go index 7398772bd2..3a973494bc 100644 --- a/misc/cgo/test/backdoor/backdoor.go +++ b/misc/cgo/test/backdoor/backdoor.go @@ -4,5 +4,4 @@ package backdoor -func LockedOSThread() bool // in runtime.c -func Issue7695(x1, x2, x3, x4, x5, x6, x7, x8 uintptr) +func LockedOSThread() bool // in thunk.s diff --git a/misc/cgo/test/backdoor/backdoor_gccgo.go b/misc/cgo/test/backdoor/backdoor_gccgo.go deleted file mode 100644 index 514f76ec5e..0000000000 --- a/misc/cgo/test/backdoor/backdoor_gccgo.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2014 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. - -// This is the gccgo version of the stub in runtime.c. - -// +build gccgo - -package backdoor - -func Issue7695(x1, x2, x3, x4, x5, x6, x7, x8 uintptr) {} diff --git a/misc/cgo/test/backdoor/runtime.c b/misc/cgo/test/backdoor/runtime.c deleted file mode 100644 index 87ee44eb6f..0000000000 --- a/misc/cgo/test/backdoor/runtime.c +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2011 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. - -// Expose some runtime functions for testing. -// Must be in a non-cgo-using package so that -// the go command compiles this file with 6c, not gcc. - -// +build gc - -typedef char bool; - -// This is what a cgo-compiled stub declaration looks like. -void -·Issue7695(struct{void *y[8*sizeof(void*)];}p) -{ - USED(p); -} diff --git a/misc/cgo/test/issue7695_test.go b/misc/cgo/test/issue7695_test.go deleted file mode 100644 index de2fc03d42..0000000000 --- a/misc/cgo/test/issue7695_test.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build ignore -// This test depends on running C code on Go stacks. Not allowed anymore. - -// Demo of deferred C function with untrue prototype -// breaking stack copying. See golang.org/issue/7695. - -package cgotest - -import ( - "testing" - - "./backdoor" -) - -func TestIssue7695(t *testing.T) { - defer backdoor.Issue7695(1, 0, 2, 0, 0, 3, 0, 4) - recurse(100) -} - -func recurse(n int) { - var x [128]int - n += x[0] - if n > 0 { - recurse(n - 1) - } -} diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index 63875aed5a..4bbcc2b971 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -614,6 +614,16 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package } p.Target = p.target + // Check for C code compiled with Plan 9 C compiler. + // No longer allowed except in runtime and runtime/cgo, for now. + if len(p.CFiles) > 0 && !p.usesCgo() && (!p.Standard || p.ImportPath != "runtime") { + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("C source files not allowed when not using cgo: %s", strings.Join(p.CFiles, " ")), + } + return p + } + // In the absence of errors lower in the dependency tree, // check for case-insensitive collisions of import paths. if len(p.DepsErrors) == 0 { diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 13886e158b..9ae17e1054 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -157,6 +157,20 @@ fi rm -f ./testdata/err unset GOPATH +export GOPATH=$(pwd)/testdata/src +TEST disallowed C source files +export GOPATH=$(pwd)/testdata +if ./testgo build badc 2>testdata/err; then + echo 'go build badc succeeded' + ok=false +elif ! grep 'C source files not allowed' testdata/err >/dev/null; then + echo 'go test did not say C source files not allowed:' + cat testdata/err + ok=false +fi +rm -f ./testdata/err +unset GOPATH + TEST error message for syntax error in test go file says FAIL export GOPATH=$(pwd)/testdata if ./testgo test syntaxerror 2>testdata/err; then diff --git a/src/cmd/go/testdata/src/badc/x.c b/src/cmd/go/testdata/src/badc/x.c new file mode 100644 index 0000000000..f6cbf6924d --- /dev/null +++ b/src/cmd/go/testdata/src/badc/x.c @@ -0,0 +1 @@ +// C code! diff --git a/src/cmd/go/testdata/src/badc/x.go b/src/cmd/go/testdata/src/badc/x.go new file mode 100644 index 0000000000..bfa1de28bd --- /dev/null +++ b/src/cmd/go/testdata/src/badc/x.go @@ -0,0 +1 @@ +package badc diff --git a/src/net/empty.c b/src/net/empty.c deleted file mode 100644 index a515c2fe29..0000000000 --- a/src/net/empty.c +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2013 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. - -// This file is required to prevent compiler errors -// when the package built with CGO_ENABLED=0. -// Otherwise the compiler says: -// pkg/net/fd_poll_runtime.go:15: missing function body diff --git a/src/net/empty.s b/src/net/empty.s new file mode 100644 index 0000000000..a515c2fe29 --- /dev/null +++ b/src/net/empty.s @@ -0,0 +1,8 @@ +// Copyright 2013 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. + +// This file is required to prevent compiler errors +// when the package built with CGO_ENABLED=0. +// Otherwise the compiler says: +// pkg/net/fd_poll_runtime.go:15: missing function body diff --git a/src/runtime/debug/debug.c b/src/runtime/debug/debug.c deleted file mode 100644 index a7292c477b..0000000000 --- a/src/runtime/debug/debug.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2013 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. - -// Nothing to see here. -// This file exists so that the go command knows that parts of the -// package are implemented in C, so that it does not instruct the -// Go compiler to complain about extern declarations. -// The actual implementations are in package runtime. diff --git a/src/runtime/debug/debug.s b/src/runtime/debug/debug.s new file mode 100644 index 0000000000..a7292c477b --- /dev/null +++ b/src/runtime/debug/debug.s @@ -0,0 +1,9 @@ +// Copyright 2013 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. + +// Nothing to see here. +// This file exists so that the go command knows that parts of the +// package are implemented in C, so that it does not instruct the +// Go compiler to complain about extern declarations. +// The actual implementations are in package runtime. -- cgit v1.3 From 00d2f916adf2fb025406f0558ab8cac122fdf060 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 15:20:03 -0400 Subject: cmd/gc: run escape analysis always (even in -N mode) Fixes #8585. Removes some little-used code paths. LGTM=josharian R=golang-codereviews, minux, josharian CC=golang-codereviews, iant, r https://golang.org/cl/132970043 --- src/cmd/gc/gen.c | 6 +- src/cmd/gc/lex.c | 8 +- src/cmd/gc/typecheck.c | 6 - test/escape2.go | 2 + test/escape2n.go | 1494 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1503 insertions(+), 13 deletions(-) create mode 100644 test/escape2n.go (limited to 'src') diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index 83c46c6504..eb9eacca8f 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -54,9 +54,6 @@ addrescapes(Node *n) if(n->class == PAUTO && n->esc == EscNever) break; - if(debug['N'] && n->esc != EscUnknown) - fatal("without escape analysis, only PAUTO's should have esc: %N", n); - switch(n->class) { case PPARAMREF: addrescapes(n->defn); @@ -91,8 +88,7 @@ addrescapes(Node *n) snprint(buf, sizeof buf, "&%S", n->sym); n->heapaddr->sym = lookup(buf); n->heapaddr->orig->sym = n->heapaddr->sym; - if(!debug['N']) - n->esc = EscHeap; + n->esc = EscHeap; if(debug['m']) print("%L: moved to heap: %N\n", n->lineno, n); curfn = oldfn; diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 6d83177477..2303b442cd 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -481,8 +481,12 @@ main(int argc, char *argv[]) } // Phase 5: Escape analysis. - if(!debug['N']) - escapes(xtop); + // Required for moving heap allocations onto stack, + // which in turn is required by the closure implementation, + // which stores the addresses of stack variables into the closure. + // If the closure does not escape, it needs to be on the stack + // or else the stack copier will not update it. + escapes(xtop); // Escape analysis moved escaped values off stack. // Move large values off stack too. diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 18d20cdd16..298920bfec 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -736,10 +736,6 @@ reswitch: l = n->left; if((t = l->type) == T) goto error; - // top&Eindir means this is &x in *&x. (or the arg to built-in print) - // n->etype means code generator flagged it as non-escaping. - if(debug['N'] && !(top & Eindir) && !n->etype) - addrescapes(n->left); n->type = ptrto(t); goto ret; @@ -2119,8 +2115,6 @@ lookdot(Node *n, Type *t, int dostrcmp) if(!eqtype(rcvr, tt)) { if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) { checklvalue(n->left, "call pointer method on"); - if(debug['N']) - addrescapes(n->left); n->left = nod(OADDR, n->left, N); n->left->implicit = 1; typecheck(&n->left, Etype|Erv); diff --git a/test/escape2.go b/test/escape2.go index 28251aa98b..6a46ce86ab 100644 --- a/test/escape2.go +++ b/test/escape2.go @@ -7,6 +7,8 @@ // Test, using compiler diagnostic flags, that the escape analysis is working. // Compiles but does not run. Inlining is disabled. +// escape2n.go contains all the same tests but compiles with -N. + package foo import ( diff --git a/test/escape2n.go b/test/escape2n.go new file mode 100644 index 0000000000..002a78ea50 --- /dev/null +++ b/test/escape2n.go @@ -0,0 +1,1494 @@ +// errorcheck -0 -N -m -l + +// Copyright 2010 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. + +// Test, using compiler diagnostic flags, that the escape analysis is working. +// Compiles but does not run. Inlining is disabled. +// Registerization is disabled too (-N), which should +// have no effect on escape analysis. + +package foo + +import ( + "fmt" + "unsafe" +) + +var gxx *int + +func foo1(x int) { // ERROR "moved to heap: x" + gxx = &x // ERROR "&x escapes to heap" +} + +func foo2(yy *int) { // ERROR "leaking param: yy" + gxx = yy +} + +func foo3(x int) *int { // ERROR "moved to heap: x" + return &x // ERROR "&x escapes to heap" +} + +type T *T + +func foo3b(t T) { // ERROR "leaking param: t" + *t = t +} + +// xx isn't going anywhere, so use of yy is ok +func foo4(xx, yy *int) { // ERROR "xx does not escape" "yy does not escape" + xx = yy +} + +// xx isn't going anywhere, so taking address of yy is ok +func foo5(xx **int, yy *int) { // ERROR "xx does not escape" "yy does not escape" + xx = &yy // ERROR "&yy does not escape" +} + +func foo6(xx **int, yy *int) { // ERROR "xx does not escape" "leaking param: yy" + *xx = yy +} + +func foo7(xx **int, yy *int) { // ERROR "xx does not escape" "yy does not escape" + **xx = *yy +} + +func foo8(xx, yy *int) int { // ERROR "xx does not escape" "yy does not escape" + xx = yy + return *xx +} + +func foo9(xx, yy *int) *int { // ERROR "leaking param: xx" "leaking param: yy" + xx = yy + return xx +} + +func foo10(xx, yy *int) { // ERROR "xx does not escape" "yy does not escape" + *xx = *yy +} + +func foo11() int { + x, y := 0, 42 + xx := &x // ERROR "&x does not escape" + yy := &y // ERROR "&y does not escape" + *xx = *yy + return x +} + +var xxx **int + +func foo12(yyy **int) { // ERROR "leaking param: yyy" + xxx = yyy +} + +// Must treat yyy as leaking because *yyy leaks, and the escape analysis +// summaries in exported metadata do not distinguish these two cases. +func foo13(yyy **int) { // ERROR "leaking param: yyy" + *xxx = *yyy +} + +func foo14(yyy **int) { // ERROR "yyy does not escape" + **xxx = **yyy +} + +func foo15(yy *int) { // ERROR "moved to heap: yy" + xxx = &yy // ERROR "&yy escapes to heap" +} + +func foo16(yy *int) { // ERROR "leaking param: yy" + *xxx = yy +} + +func foo17(yy *int) { // ERROR "yy does not escape" + **xxx = *yy +} + +func foo18(y int) { // ERROR "moved to heap: "y" + *xxx = &y // ERROR "&y escapes to heap" +} + +func foo19(y int) { + **xxx = y +} + +type Bar struct { + i int + ii *int +} + +func NewBar() *Bar { + return &Bar{42, nil} // ERROR "&Bar literal escapes to heap" +} + +func NewBarp(x *int) *Bar { // ERROR "leaking param: x" + return &Bar{42, x} // ERROR "&Bar literal escapes to heap" +} + +func NewBarp2(x *int) *Bar { // ERROR "x does not escape" + return &Bar{*x, nil} // ERROR "&Bar literal escapes to heap" +} + +func (b *Bar) NoLeak() int { // ERROR "b does not escape" + return *(b.ii) +} + +func (b *Bar) Leak() *int { // ERROR "leaking param: b" + return &b.i // ERROR "&b.i escapes to heap" +} + +func (b *Bar) AlsoNoLeak() *int { // ERROR "leaking param b content to result ~r0" + return b.ii +} + +func (b Bar) AlsoLeak() *int { // ERROR "leaking param: b" + return b.ii +} + +func (b Bar) LeaksToo() *int { // ERROR "leaking param: b" + v := 0 // ERROR "moved to heap: v" + b.ii = &v // ERROR "&v escapes" + return b.ii +} + +func (b *Bar) LeaksABit() *int { // ERROR "leaking param b content to result ~r0" + v := 0 // ERROR "moved to heap: v" + b.ii = &v // ERROR "&v escapes" + return b.ii +} + +func (b Bar) StillNoLeak() int { // ERROR "b does not escape" + v := 0 + b.ii = &v // ERROR "&v does not escape" + return b.i +} + +func goLeak(b *Bar) { // ERROR "leaking param: b" + go b.NoLeak() +} + +type Bar2 struct { + i [12]int + ii []int +} + +func NewBar2() *Bar2 { + return &Bar2{[12]int{42}, nil} // ERROR "&Bar2 literal escapes to heap" +} + +func (b *Bar2) NoLeak() int { // ERROR "b does not escape" + return b.i[0] +} + +func (b *Bar2) Leak() []int { // ERROR "leaking param: b" + return b.i[:] // ERROR "b.i escapes to heap" +} + +func (b *Bar2) AlsoNoLeak() []int { // ERROR "leaking param b content to result ~r0" + return b.ii[0:1] +} + +func (b Bar2) AgainNoLeak() [12]int { // ERROR "b does not escape" + return b.i +} + +func (b *Bar2) LeakSelf() { // ERROR "leaking param: b" + b.ii = b.i[0:4] // ERROR "b.i escapes to heap" +} + +func (b *Bar2) LeakSelf2() { // ERROR "leaking param: b" + var buf []int + buf = b.i[0:] // ERROR "b.i escapes to heap" + b.ii = buf +} + +func foo21() func() int { + x := 42 // ERROR "moved to heap: x" + return func() int { // ERROR "func literal escapes to heap" + return x // ERROR "&x escapes to heap" + } +} + +func foo22() int { + x := 42 + return func() int { // ERROR "func literal does not escape" + return x + }() +} + +func foo23(x int) func() int { // ERROR "moved to heap: x" + return func() int { // ERROR "func literal escapes to heap" + return x // ERROR "&x escapes to heap" + } +} + +func foo23a(x int) func() int { // ERROR "moved to heap: x" + f := func() int { // ERROR "func literal escapes to heap" + return x // ERROR "&x escapes to heap" + } + return f +} + +func foo23b(x int) *(func() int) { // ERROR "moved to heap: x" + f := func() int { return x } // ERROR "moved to heap: f" "func literal escapes to heap" "&x escapes to heap" + return &f // ERROR "&f escapes to heap" +} + +func foo24(x int) int { + return func() int { // ERROR "func literal does not escape" + return x + }() +} + +var x *int + +func fooleak(xx *int) int { // ERROR "leaking param: xx" + x = xx + return *x +} + +func foonoleak(xx *int) int { // ERROR "xx does not escape" + return *x + *xx +} + +func foo31(x int) int { // ERROR "moved to heap: x" + return fooleak(&x) // ERROR "&x escapes to heap" +} + +func foo32(x int) int { + return foonoleak(&x) // ERROR "&x does not escape" +} + +type Foo struct { + xx *int + x int +} + +var F Foo +var pf *Foo + +func (f *Foo) fooleak() { // ERROR "leaking param: f" + pf = f +} + +func (f *Foo) foonoleak() { // ERROR "f does not escape" + F.x = f.x +} + +func (f *Foo) Leak() { // ERROR "leaking param: f" + f.fooleak() +} + +func (f *Foo) NoLeak() { // ERROR "f does not escape" + f.foonoleak() +} + +func foo41(x int) { // ERROR "moved to heap: x" + F.xx = &x // ERROR "&x escapes to heap" +} + +func (f *Foo) foo42(x int) { // ERROR "f does not escape" "moved to heap: x" + f.xx = &x // ERROR "&x escapes to heap" +} + +func foo43(f *Foo, x int) { // ERROR "f does not escape" "moved to heap: x" + f.xx = &x // ERROR "&x escapes to heap" +} + +func foo44(yy *int) { // ERROR "leaking param: yy" + F.xx = yy +} + +func (f *Foo) foo45() { // ERROR "f does not escape" + F.x = f.x +} + +// See foo13 above for explanation of why f leaks. +func (f *Foo) foo46() { // ERROR "leaking param: f" + F.xx = f.xx +} + +func (f *Foo) foo47() { // ERROR "leaking param: f" + f.xx = &f.x // ERROR "&f.x escapes to heap" +} + +var ptrSlice []*int + +func foo50(i *int) { // ERROR "leaking param: i" + ptrSlice[0] = i +} + +var ptrMap map[*int]*int + +func foo51(i *int) { // ERROR "leaking param: i" + ptrMap[i] = i +} + +func indaddr1(x int) *int { // ERROR "moved to heap: x" + return &x // ERROR "&x escapes to heap" +} + +func indaddr2(x *int) *int { // ERROR "leaking param: x" + return *&x // ERROR "&x does not escape" +} + +func indaddr3(x *int32) *int { // ERROR "leaking param: x" + return *(**int)(unsafe.Pointer(&x)) // ERROR "&x does not escape" +} + +// From package math: + +func Float32bits(f float32) uint32 { + return *(*uint32)(unsafe.Pointer(&f)) // ERROR "&f does not escape" +} + +func Float32frombits(b uint32) float32 { + return *(*float32)(unsafe.Pointer(&b)) // ERROR "&b does not escape" +} + +func Float64bits(f float64) uint64 { + return *(*uint64)(unsafe.Pointer(&f)) // ERROR "&f does not escape" +} + +func Float64frombits(b uint64) float64 { + return *(*float64)(unsafe.Pointer(&b)) // ERROR "&b does not escape" +} + +// contrast with +func float64bitsptr(f float64) *uint64 { // ERROR "moved to heap: f" + return (*uint64)(unsafe.Pointer(&f)) // ERROR "&f escapes to heap" +} + +func float64ptrbitsptr(f *float64) *uint64 { // ERROR "leaking param: f" + return (*uint64)(unsafe.Pointer(f)) +} + +func typesw(i interface{}) *int { // ERROR "leaking param: i" + switch val := i.(type) { + case *int: + return val + case *int8: + v := int(*val) // ERROR "moved to heap: v" + return &v // ERROR "&v escapes to heap" + } + return nil +} + +func exprsw(i *int) *int { // ERROR "leaking param: i" + switch j := i; *j + 110 { + case 12: + return j + case 42: + return nil + } + return nil + +} + +// assigning to an array element is like assigning to the array +func foo60(i *int) *int { // ERROR "leaking param: i" + var a [12]*int + a[0] = i + return a[1] +} + +func foo60a(i *int) *int { // ERROR "i does not escape" + var a [12]*int + a[0] = i + return nil +} + +// assigning to a struct field is like assigning to the struct +func foo61(i *int) *int { // ERROR "leaking param: i" + type S struct { + a, b *int + } + var s S + s.a = i + return s.b +} + +func foo61a(i *int) *int { // ERROR "i does not escape" + type S struct { + a, b *int + } + var s S + s.a = i + return nil +} + +// assigning to a struct field is like assigning to the struct but +// here this subtlety is lost, since s.a counts as an assignment to a +// track-losing dereference. +func foo62(i *int) *int { // ERROR "leaking param: i" + type S struct { + a, b *int + } + s := new(S) // ERROR "new[(]S[)] does not escape" + s.a = i + return nil // s.b +} + +type M interface { + M() +} + +func foo63(m M) { // ERROR "m does not escape" +} + +func foo64(m M) { // ERROR "leaking param: m" + m.M() +} + +func foo64b(m M) { // ERROR "leaking param: m" + defer m.M() +} + +type MV int + +func (MV) M() {} + +func foo65() { + var mv MV + foo63(&mv) // ERROR "&mv does not escape" +} + +func foo66() { + var mv MV // ERROR "moved to heap: mv" + foo64(&mv) // ERROR "&mv escapes to heap" +} + +func foo67() { + var mv MV + foo63(mv) +} + +func foo68() { + var mv MV + foo64(mv) // escapes but it's an int so irrelevant +} + +func foo69(m M) { // ERROR "leaking param: m" + foo64(m) +} + +func foo70(mv1 *MV, m M) { // ERROR "leaking param: mv1" "leaking param: m" + m = mv1 + foo64(m) +} + +func foo71(x *int) []*int { // ERROR "leaking param: x" + var y []*int + y = append(y, x) + return y +} + +func foo71a(x int) []*int { // ERROR "moved to heap: x" + var y []*int + y = append(y, &x) // ERROR "&x escapes to heap" + return y +} + +func foo72() { + var x int + var y [1]*int + y[0] = &x // ERROR "&x does not escape" +} + +func foo72aa() [10]*int { + var x int // ERROR "moved to heap: x" + var y [10]*int + y[0] = &x // ERROR "&x escapes to heap" + return y +} + +func foo72a() { + var y [10]*int + for i := 0; i < 10; i++ { + // escapes its scope + x := i // ERROR "moved to heap: x" + y[i] = &x // ERROR "&x escapes to heap" + } + return +} + +func foo72b() [10]*int { + var y [10]*int + for i := 0; i < 10; i++ { + x := i // ERROR "moved to heap: x" + y[i] = &x // ERROR "&x escapes to heap" + } + return y +} + +// issue 2145 +func foo73() { + s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape" + for _, v := range s { + vv := v // ERROR "moved to heap: vv" + // actually just escapes its scope + defer func() { // ERROR "func literal escapes to heap" + println(vv) // ERROR "&vv escapes to heap" + }() + } +} + +func foo74() { + s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape" + for _, v := range s { + vv := v // ERROR "moved to heap: vv" + // actually just escapes its scope + fn := func() { // ERROR "func literal escapes to heap" + println(vv) // ERROR "&vv escapes to heap" + } + defer fn() + } +} + +// issue 3975 +func foo74b() { + var array [3]func() + s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape" + for i, v := range s { + vv := v // ERROR "moved to heap: vv" + // actually just escapes its scope + array[i] = func() { // ERROR "func literal escapes to heap" + println(vv) // ERROR "&vv escapes to heap" + } + } +} + +func myprint(y *int, x ...interface{}) *int { // ERROR "x does not escape" "leaking param: y" + return y +} + +func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "y does not escape" "leaking param: x" + return &x[0] // ERROR "&x.0. escapes to heap" +} + +func foo75(z *int) { // ERROR "z does not escape" + myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" +} + +func foo75a(z *int) { // ERROR "z does not escape" + myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" +} + +func foo75esc(z *int) { // ERROR "leaking param: z" + gxx = myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" +} + +func foo75aesc(z *int) { // ERROR "z does not escape" + var ppi **interface{} // assignments to pointer dereferences lose track + *ppi = myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap" +} + +func foo76(z *int) { // ERROR "leaking param: z" + myprint(nil, z) // ERROR "[.][.][.] argument does not escape" +} + +func foo76a(z *int) { // ERROR "leaking param: z" + myprint1(nil, z) // ERROR "[.][.][.] argument does not escape" +} + +func foo76b() { + myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" +} + +func foo76c() { + myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" +} + +func foo76d() { + defer myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" +} + +func foo76e() { + defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" +} + +func foo76f() { + for { + // TODO: This one really only escapes its scope, but we don't distinguish yet. + defer myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap" + } +} + +func foo76g() { + for { + defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap" + } +} + +func foo77(z []interface{}) { // ERROR "z does not escape" + myprint(nil, z...) // z does not escape +} + +func foo77a(z []interface{}) { // ERROR "z does not escape" + myprint1(nil, z...) +} + +func foo77b(z []interface{}) { // ERROR "leaking param: z" + var ppi **interface{} + *ppi = myprint1(nil, z...) +} + +func foo78(z int) *int { // ERROR "moved to heap: z" + return &z // ERROR "&z escapes to heap" +} + +func foo78a(z int) *int { // ERROR "moved to heap: z" + y := &z // ERROR "&z escapes to heap" + x := &y // ERROR "&y does not escape" + return *x // really return y +} + +func foo79() *int { + return new(int) // ERROR "new[(]int[)] escapes to heap" +} + +func foo80() *int { + var z *int + for { + // Really just escapes its scope but we don't distinguish + z = new(int) // ERROR "new[(]int[)] escapes to heap" + } + _ = z + return nil +} + +func foo81() *int { + for { + z := new(int) // ERROR "new[(]int[)] does not escape" + _ = z + } + return nil +} + +func tee(p *int) (x, y *int) { return p, p } // ERROR "leaking param" + +func noop(x, y *int) {} // ERROR "does not escape" + +func foo82() { + var x, y, z int // ERROR "moved to heap" + go noop(tee(&z)) // ERROR "&z escapes to heap" + go noop(&x, &y) // ERROR "escapes to heap" + for { + var u, v, w int // ERROR "moved to heap" + defer noop(tee(&u)) // ERROR "&u escapes to heap" + defer noop(&v, &w) // ERROR "escapes to heap" + } +} + +type Fooer interface { + Foo() +} + +type LimitedFooer struct { + Fooer + N int64 +} + +func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: r" + return &LimitedFooer{r, n} // ERROR "&LimitedFooer literal escapes to heap" +} + +func foo90(x *int) map[*int]*int { // ERROR "leaking param: x" + return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal escapes to heap" +} + +func foo91(x *int) map[*int]*int { // ERROR "leaking param: x" + return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal escapes to heap" +} + +func foo92(x *int) [2]*int { // ERROR "leaking param: x" + return [2]*int{x, nil} +} + +// does not leak c +func foo93(c chan *int) *int { // ERROR "c does not escape" + for v := range c { + return v + } + return nil +} + +// does not leak m +func foo94(m map[*int]*int, b bool) *int { // ERROR "m does not escape" + for k, v := range m { + if b { + return k + } + return v + } + return nil +} + +// does leak x +func foo95(m map[*int]*int, x *int) { // ERROR "m does not escape" "leaking param: x" + m[x] = x +} + +// does not leak m +func foo96(m []*int) *int { // ERROR "m does not escape" + return m[0] +} + +// does leak m +func foo97(m [1]*int) *int { // ERROR "leaking param: m" + return m[0] +} + +// does not leak m +func foo98(m map[int]*int) *int { // ERROR "m does not escape" + return m[0] +} + +// does leak m +func foo99(m *[1]*int) []*int { // ERROR "leaking param: m" + return m[:] +} + +// does not leak m +func foo100(m []*int) *int { // ERROR "m does not escape" + for _, v := range m { + return v + } + return nil +} + +// does leak m +func foo101(m [1]*int) *int { // ERROR "leaking param: m" + for _, v := range m { + return v + } + return nil +} + +// does not leak m +func foo101a(m [1]*int) *int { // ERROR "m does not escape" + for i := range m { // ERROR "moved to heap: i" + return &i // ERROR "&i escapes to heap" + } + return nil +} + +// does leak x +func foo102(m []*int, x *int) { // ERROR "m does not escape" "leaking param: x" + m[0] = x +} + +// does not leak x +func foo103(m [1]*int, x *int) { // ERROR "m does not escape" "x does not escape" + m[0] = x +} + +var y []*int + +// does not leak x +func foo104(x []*int) { // ERROR "x does not escape" + copy(y, x) +} + +// does not leak x +func foo105(x []*int) { // ERROR "x does not escape" + _ = append(y, x...) +} + +// does leak x +func foo106(x *int) { // ERROR "leaking param: x" + _ = append(y, x) +} + +func foo107(x *int) map[*int]*int { // ERROR "leaking param: x" + return map[*int]*int{x: nil} // ERROR "map.* literal escapes to heap" +} + +func foo108(x *int) map[*int]*int { // ERROR "leaking param: x" + return map[*int]*int{nil: x} // ERROR "map.* literal escapes to heap" +} + +func foo109(x *int) *int { // ERROR "leaking param: x" + m := map[*int]*int{x: nil} // ERROR "map.* literal does not escape" + for k, _ := range m { + return k + } + return nil +} + +func foo110(x *int) *int { // ERROR "leaking param: x" + m := map[*int]*int{nil: x} // ERROR "map.* literal does not escape" + return m[nil] +} + +func foo111(x *int) *int { // ERROR "leaking param: x" + m := []*int{x} // ERROR "\[\]\*int literal does not escape" + return m[0] +} + +func foo112(x *int) *int { // ERROR "leaking param: x" + m := [1]*int{x} + return m[0] +} + +func foo113(x *int) *int { // ERROR "leaking param: x" + m := Bar{ii: x} + return m.ii +} + +func foo114(x *int) *int { // ERROR "leaking param: x" + m := &Bar{ii: x} // ERROR "&Bar literal does not escape" + return m.ii +} + +func foo115(x *int) *int { // ERROR "leaking param: x" + return (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(x)) + 1)) +} + +func foo116(b bool) *int { + if b { + x := 1 // ERROR "moved to heap: x" + return &x // ERROR "&x escapes to heap" + } else { + y := 1 // ERROR "moved to heap: y" + return &y // ERROR "&y escapes to heap" + } + return nil +} + +func foo117(unknown func(interface{})) { // ERROR "unknown does not escape" + x := 1 // ERROR "moved to heap: x" + unknown(&x) // ERROR "&x escapes to heap" +} + +func foo118(unknown func(*int)) { // ERROR "unknown does not escape" + x := 1 // ERROR "moved to heap: x" + unknown(&x) // ERROR "&x escapes to heap" +} + +func external(*int) + +func foo119(x *int) { // ERROR "leaking param: x" + external(x) +} + +func foo120() { + // formerly exponential time analysis +L1: +L2: +L3: +L4: +L5: +L6: +L7: +L8: +L9: +L10: +L11: +L12: +L13: +L14: +L15: +L16: +L17: +L18: +L19: +L20: +L21: +L22: +L23: +L24: +L25: +L26: +L27: +L28: +L29: +L30: +L31: +L32: +L33: +L34: +L35: +L36: +L37: +L38: +L39: +L40: +L41: +L42: +L43: +L44: +L45: +L46: +L47: +L48: +L49: +L50: +L51: +L52: +L53: +L54: +L55: +L56: +L57: +L58: +L59: +L60: +L61: +L62: +L63: +L64: +L65: +L66: +L67: +L68: +L69: +L70: +L71: +L72: +L73: +L74: +L75: +L76: +L77: +L78: +L79: +L80: +L81: +L82: +L83: +L84: +L85: +L86: +L87: +L88: +L89: +L90: +L91: +L92: +L93: +L94: +L95: +L96: +L97: +L98: +L99: +L100: + // use the labels to silence compiler errors + goto L1 + goto L2 + goto L3 + goto L4 + goto L5 + goto L6 + goto L7 + goto L8 + goto L9 + goto L10 + goto L11 + goto L12 + goto L13 + goto L14 + goto L15 + goto L16 + goto L17 + goto L18 + goto L19 + goto L20 + goto L21 + goto L22 + goto L23 + goto L24 + goto L25 + goto L26 + goto L27 + goto L28 + goto L29 + goto L30 + goto L31 + goto L32 + goto L33 + goto L34 + goto L35 + goto L36 + goto L37 + goto L38 + goto L39 + goto L40 + goto L41 + goto L42 + goto L43 + goto L44 + goto L45 + goto L46 + goto L47 + goto L48 + goto L49 + goto L50 + goto L51 + goto L52 + goto L53 + goto L54 + goto L55 + goto L56 + goto L57 + goto L58 + goto L59 + goto L60 + goto L61 + goto L62 + goto L63 + goto L64 + goto L65 + goto L66 + goto L67 + goto L68 + goto L69 + goto L70 + goto L71 + goto L72 + goto L73 + goto L74 + goto L75 + goto L76 + goto L77 + goto L78 + goto L79 + goto L80 + goto L81 + goto L82 + goto L83 + goto L84 + goto L85 + goto L86 + goto L87 + goto L88 + goto L89 + goto L90 + goto L91 + goto L92 + goto L93 + goto L94 + goto L95 + goto L96 + goto L97 + goto L98 + goto L99 + goto L100 +} + +func foo121() { + for i := 0; i < 10; i++ { + defer myprint(nil, i) // ERROR "[.][.][.] argument escapes to heap" + go myprint(nil, i) // ERROR "[.][.][.] argument escapes to heap" + } +} + +// same as foo121 but check across import +func foo121b() { + for i := 0; i < 10; i++ { + defer fmt.Printf("%d", i) // ERROR "[.][.][.] argument escapes to heap" + go fmt.Printf("%d", i) // ERROR "[.][.][.] argument escapes to heap" + } +} + +// a harmless forward jump +func foo122() { + var i *int + + goto L1 +L1: + i = new(int) // ERROR "new.int. does not escape" + _ = i +} + +// a backward jump, increases loopdepth +func foo123() { + var i *int + +L1: + i = new(int) // ERROR "new.int. escapes to heap" + + goto L1 + _ = i +} + +func foo124(x **int) { // ERROR "x does not escape" + var i int // ERROR "moved to heap: i" + p := &i // ERROR "&i escapes" + func() { // ERROR "func literal does not escape" + *x = p // ERROR "leaking closure reference p" + }() +} + +func foo125(ch chan *int) { // ERROR "does not escape" + var i int // ERROR "moved to heap" + p := &i // ERROR "&i escapes to heap" + func() { // ERROR "func literal does not escape" + ch <- p // ERROR "leaking closure reference p" + }() +} + +func foo126() { + var px *int // loopdepth 0 + for { + // loopdepth 1 + var i int // ERROR "moved to heap" + func() { // ERROR "func literal does not escape" + px = &i // ERROR "&i escapes" + }() + } + _ = px +} + +var px *int + +func foo127() { + var i int // ERROR "moved to heap: i" + p := &i // ERROR "&i escapes to heap" + q := p + px = q +} + +func foo128() { + var i int + p := &i // ERROR "&i does not escape" + q := p + _ = q +} + +func foo129() { + var i int // ERROR "moved to heap: i" + p := &i // ERROR "&i escapes to heap" + func() { // ERROR "func literal does not escape" + q := p // ERROR "leaking closure reference p" + func() { // ERROR "func literal does not escape" + r := q // ERROR "leaking closure reference q" + px = r + }() + }() +} + +func foo130() { + for { + var i int // ERROR "moved to heap" + func() { // ERROR "func literal does not escape" + px = &i // ERROR "&i escapes" "leaking closure reference i" + }() + } +} + +func foo131() { + var i int // ERROR "moved to heap" + func() { // ERROR "func literal does not escape" + px = &i // ERROR "&i escapes" "leaking closure reference i" + }() +} + +func foo132() { + var i int // ERROR "moved to heap" + go func() { // ERROR "func literal escapes to heap" + px = &i // ERROR "&i escapes" "leaking closure reference i" + }() +} + +func foo133() { + var i int // ERROR "moved to heap" + defer func() { // ERROR "func literal does not escape" + px = &i // ERROR "&i escapes" "leaking closure reference i" + }() +} + +func foo134() { + var i int + p := &i // ERROR "&i does not escape" + func() { // ERROR "func literal does not escape" + q := p + func() { // ERROR "func literal does not escape" + r := q + _ = r + }() + }() +} + +func foo135() { + var i int // ERROR "moved to heap: i" + p := &i // ERROR "&i escapes to heap" "moved to heap: p" + go func() { // ERROR "func literal escapes to heap" + q := p // ERROR "&p escapes to heap" + func() { // ERROR "func literal does not escape" + r := q + _ = r + }() + }() +} + +func foo136() { + var i int // ERROR "moved to heap: i" + p := &i // ERROR "&i escapes to heap" "moved to heap: p" + go func() { // ERROR "func literal escapes to heap" + q := p // ERROR "&p escapes to heap" "leaking closure reference p" + func() { // ERROR "func literal does not escape" + r := q // ERROR "leaking closure reference q" + px = r + }() + }() +} + +func foo137() { + var i int // ERROR "moved to heap: i" + p := &i // ERROR "&i escapes to heap" + func() { // ERROR "func literal does not escape" + q := p // ERROR "leaking closure reference p" "moved to heap: q" + go func() { // ERROR "func literal escapes to heap" + r := q // ERROR "&q escapes to heap" + _ = r + }() + }() +} + +func foo138() *byte { + type T struct { + x [1]byte + } + t := new(T) // ERROR "new.T. escapes to heap" + return &t.x[0] // ERROR "&t.x.0. escapes to heap" +} + +func foo139() *byte { + type T struct { + x struct { + y byte + } + } + t := new(T) // ERROR "new.T. escapes to heap" + return &t.x.y // ERROR "&t.x.y escapes to heap" +} + +// issue 4751 +func foo140() interface{} { + type T struct { + X string + } + type U struct { + X string + T *T + } + t := &T{} // ERROR "&T literal escapes to heap" + return U{ + X: t.X, + T: t, + } +} + +//go:noescape + +func F1([]byte) + +func F2([]byte) + +//go:noescape + +func F3(x []byte) // ERROR "F3 x does not escape" + +func F4(x []byte) + +func G() { + var buf1 [10]byte + F1(buf1[:]) // ERROR "buf1 does not escape" + + var buf2 [10]byte // ERROR "moved to heap: buf2" + F2(buf2[:]) // ERROR "buf2 escapes to heap" + + var buf3 [10]byte + F3(buf3[:]) // ERROR "buf3 does not escape" + + var buf4 [10]byte // ERROR "moved to heap: buf4" + F4(buf4[:]) // ERROR "buf4 escapes to heap" +} + +type Tm struct { + x int +} + +func (t *Tm) M() { // ERROR "t does not escape" +} + +func foo141() { + var f func() + + t := new(Tm) // ERROR "escapes to heap" + f = t.M // ERROR "t.M does not escape" + _ = f +} + +var gf func() + +func foo142() { + t := new(Tm) // ERROR "escapes to heap" + gf = t.M // ERROR "t.M escapes to heap" +} + +// issue 3888. +func foo143() { + for i := 0; i < 1000; i++ { + func() { // ERROR "func literal does not escape" + for i := 0; i < 1; i++ { + var t Tm + t.M() // ERROR "t does not escape" + } + }() + } +} + +// issue 5773 +// Check that annotations take effect regardless of whether they +// are before or after the use in the source code. + +//go:noescape + +func foo144a(*int) + +func foo144() { + var x int + foo144a(&x) // ERROR "&x does not escape" + var y int + foo144b(&y) // ERROR "&y does not escape" +} + +//go:noescape + +func foo144b(*int) + +// issue 7313: for loop init should not be treated as "in loop" + +type List struct { + Next *List +} + +func foo145(l List) { // ERROR "l does not escape" + var p *List + for p = &l; p.Next != nil; p = p.Next { // ERROR "&l does not escape" + } +} + +func foo146(l List) { // ERROR "l does not escape" + var p *List + p = &l // ERROR "&l does not escape" + for ; p.Next != nil; p = p.Next { + } +} + +func foo147(l List) { // ERROR "l does not escape" + var p *List + p = &l // ERROR "&l does not escape" + for p.Next != nil { + p = p.Next + } +} + +func foo148(l List) { // ERROR " l does not escape" + for p := &l; p.Next != nil; p = p.Next { // ERROR "&l does not escape" + } +} + +// related: address of variable should have depth of variable, not of loop + +func foo149(l List) { // ERROR " l does not escape" + var p *List + for { + for p = &l; p.Next != nil; p = p.Next { // ERROR "&l does not escape" + } + } +} + +// issue 7934: missed ... if element type had no pointers + +var save150 []byte + +func foo150(x ...byte) { // ERROR "leaking param: x" + save150 = x +} + +func bar150() { + foo150(1, 2, 3) // ERROR "[.][.][.] argument escapes to heap" +} + +// issue 7931: bad handling of slice of array + +var save151 *int + +func foo151(x *int) { // ERROR "leaking param: x" + save151 = x +} + +func bar151() { + var a [64]int // ERROR "moved to heap: a" + a[4] = 101 + foo151(&(&a)[4:8][0]) // ERROR "&\(&a\)\[4:8\]\[0\] escapes to heap" "&a escapes to heap" +} + +func bar151b() { + var a [10]int // ERROR "moved to heap: a" + b := a[:] // ERROR "a escapes to heap" + foo151(&b[4:8][0]) // ERROR "&b\[4:8\]\[0\] escapes to heap" +} + +func bar151c() { + var a [64]int // ERROR "moved to heap: a" + a[4] = 101 + foo151(&(&a)[4:8:8][0]) // ERROR "&\(&a\)\[4:8:8\]\[0\] escapes to heap" "&a escapes to heap" +} + +func bar151d() { + var a [10]int // ERROR "moved to heap: a" + b := a[:] // ERROR "a escapes to heap" + foo151(&b[4:8:8][0]) // ERROR "&b\[4:8:8\]\[0\] escapes to heap" +} + +// issue 8120 + +type U struct { + s *string +} + +func (u *U) String() *string { // ERROR "leaking param u content to result ~r0" + return u.s +} + +type V struct { + s *string +} + +func NewV(u U) *V { // ERROR "leaking param: u" + return &V{u.String()} // ERROR "&V literal escapes to heap" "u does not escape" +} + +func foo152() { + a := "a" // ERROR "moved to heap: a" + u := U{&a} // ERROR "&a escapes to heap" + v := NewV(u) + println(v) +} + +// issue 8176 - &x in type switch body not marked as escaping + +func foo153(v interface{}) *int { // ERROR "leaking param: v" + switch x := v.(type) { + case int: // ERROR "moved to heap: x" + return &x // ERROR "&x escapes to heap" + } + panic(0) +} + +// issue 8185 - &result escaping into result + +func f() (x int, y *int) { // ERROR "moved to heap: x" + y = &x // ERROR "&x escapes to heap" + return +} + +func g() (x interface{}) { // ERROR "moved to heap: x" + x = &x // ERROR "&x escapes to heap" + return +} -- cgit v1.3 From fb4e185a99f3e93bc1bbe08a09ebfd5419409d48 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 24 Sep 2014 12:42:47 -0700 Subject: src: pass GO_GCFLAGS down to go test std Update #8725 LGTM=rsc, josharian R=rsc, josharian CC=golang-codereviews https://golang.org/cl/149000043 --- src/run.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/run.bash b/src/run.bash index d6e53304d8..4966cf1aa6 100755 --- a/src/run.bash +++ b/src/run.bash @@ -52,7 +52,7 @@ timeout_scale=1 [ "$GOARCH" == "arm" ] && timeout_scale=3 echo '# Testing packages.' -time go test std -short -timeout=$(expr 120 \* $timeout_scale)s +time go test std -short -timeout=$(expr 120 \* $timeout_scale)s -gcflags "$GO_GCFLAGS" echo # We set GOMAXPROCS=2 in addition to -cpu=1,2,4 in order to test runtime bootstrap code, -- cgit v1.3 From 43c4287b25da53b2e8cb0de64d40689c56eb42bd Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 16:53:34 -0400 Subject: cmd/gc: fix import of package with var func returning _ Fixes #8280. LGTM=iant R=golang-codereviews, iant CC=golang-codereviews, r https://golang.org/cl/146240043 --- src/cmd/gc/fmt.c | 15 +++++---------- test/fixedbugs/issue8280.dir/a.go | 3 +++ test/fixedbugs/issue8280.dir/b.go | 5 +++++ test/fixedbugs/issue8280.go | 9 +++++++++ 4 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 test/fixedbugs/issue8280.dir/a.go create mode 100644 test/fixedbugs/issue8280.dir/b.go create mode 100644 test/fixedbugs/issue8280.go (limited to 'src') diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c index 98556a658f..f67757449b 100644 --- a/src/cmd/gc/fmt.c +++ b/src/cmd/gc/fmt.c @@ -1108,16 +1108,11 @@ exprfmt(Fmt *f, Node *n, int prec) case ONAME: // Special case: name used as local variable in export. - switch(n->class&~PHEAP){ - case PAUTO: - case PPARAM: - case PPARAMOUT: - // _ becomes ~b%d internally; print as _ for export - if(fmtmode == FExp && n->sym && n->sym->name[0] == '~' && n->sym->name[1] == 'b') - return fmtprint(f, "_"); - if(fmtmode == FExp && n->sym && !isblank(n) && n->vargen > 0) - return fmtprint(f, "%S·%d", n->sym, n->vargen); - } + // _ becomes ~b%d internally; print as _ for export + if(fmtmode == FExp && n->sym && n->sym->name[0] == '~' && n->sym->name[1] == 'b') + return fmtprint(f, "_"); + if(fmtmode == FExp && n->sym && !isblank(n) && n->vargen > 0) + return fmtprint(f, "%S·%d", n->sym, n->vargen); // Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method, // but for export, this should be rendered as (*pkg.T).meth. diff --git a/test/fixedbugs/issue8280.dir/a.go b/test/fixedbugs/issue8280.dir/a.go new file mode 100644 index 0000000000..588536e79a --- /dev/null +++ b/test/fixedbugs/issue8280.dir/a.go @@ -0,0 +1,3 @@ +package a + +var Bar = func() (_ int) { return 0 } diff --git a/test/fixedbugs/issue8280.dir/b.go b/test/fixedbugs/issue8280.dir/b.go new file mode 100644 index 0000000000..c46c554588 --- /dev/null +++ b/test/fixedbugs/issue8280.dir/b.go @@ -0,0 +1,5 @@ +package b + +import "./a" + +var foo = a.Bar diff --git a/test/fixedbugs/issue8280.go b/test/fixedbugs/issue8280.go new file mode 100644 index 0000000000..91256c852d --- /dev/null +++ b/test/fixedbugs/issue8280.go @@ -0,0 +1,9 @@ +// compiledir + +// Copyright 2014 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 8280: cannot import package exporting a func var returning a result named _ + +package ignored -- cgit v1.3 From 5917692b98695b606744f638224a82a2472bfeaa Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 16:53:47 -0400 Subject: debug/dwarf: correct name for clang-generated complex type Fixes #8694. LGTM=iant R=iant CC=golang-codereviews https://golang.org/cl/143570043 --- misc/cgo/test/cgo_test.go | 1 + misc/cgo/test/issue8694.go | 32 ++++++++++++++++++++++++++++++++ src/debug/dwarf/type.go | 11 +++++++++++ 3 files changed, 44 insertions(+) create mode 100644 misc/cgo/test/issue8694.go (limited to 'src') diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go index 1899d46053..1d1abf7291 100644 --- a/misc/cgo/test/cgo_test.go +++ b/misc/cgo/test/cgo_test.go @@ -57,5 +57,6 @@ func Test7560(t *testing.T) { test7560(t) } func Test5242(t *testing.T) { test5242(t) } func Test8092(t *testing.T) { test8092(t) } func Test7978(t *testing.T) { test7978(t) } +func Test8694(t *testing.T) { test8694(t) } func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } diff --git a/misc/cgo/test/issue8694.go b/misc/cgo/test/issue8694.go new file mode 100644 index 0000000000..643b284f6a --- /dev/null +++ b/misc/cgo/test/issue8694.go @@ -0,0 +1,32 @@ +// Copyright 2014 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 cgotest + +/* +#include + +complex float complexFloatSquared(complex float a) { return a*a; } +complex double complexDoubleSquared(complex double a) { return a*a; } +*/ +import "C" + +import "testing" + +func test8694(t *testing.T) { + // Really just testing that this compiles, but check answer anyway. + x := complex64(2 + 3i) + x2 := x * x + cx2 := C.complexFloatSquared(x) + if cx2 != x2 { + t.Errorf("C.complexFloatSquared(%v) = %v, want %v", x, cx2, x2) + } + + y := complex128(2 + 3i) + y2 := y * y + cy2 := C.complexDoubleSquared(y) + if cy2 != y2 { + t.Errorf("C.complexDoubleSquared(%v) = %v, want %v", y, cy2, y2) + } +} diff --git a/src/debug/dwarf/type.go b/src/debug/dwarf/type.go index fa40b2bef1..6986b19e72 100644 --- a/src/debug/dwarf/type.go +++ b/src/debug/dwarf/type.go @@ -431,6 +431,17 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off typ = new(BoolType) case encComplexFloat: typ = new(ComplexType) + if name == "complex" { + // clang writes out 'complex' instead of 'complex float' or 'complex double'. + // clang also writes out a byte size that we can use to distinguish. + // See issue 8694. + switch byteSize, _ := e.Val(AttrByteSize).(int64); byteSize { + case 8: + name = "complex float" + case 16: + name = "complex double" + } + } case encFloat: typ = new(FloatType) case encSigned: -- cgit v1.3 From 193daab9889708f7a20ff46efe0fa4b2bf0468d3 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 16:55:26 -0400 Subject: cmd/cc, cmd/ld, runtime: disallow conservative data/bss objects In linker, refuse to write conservative (array of pointers) as the garbage collection type for any variable in the data/bss GC program. In the linker, attach the Go type to an already-read C declaration during dedup. This gives us Go types for C globals for free as long as the cmd/dist-generated Go code contains the declaration. (Most runtime C declarations have a corresponding Go declaration. Both are bss declarations and so the linker dedups them.) In cmd/dist, add a few more C files to the auto-Go-declaration list in order to get Go type information for the C declarations into the linker. In C compiler, mark all non-pointer-containing global declarations and all string data as NOPTR. This allows them to exist in C files without any corresponding Go declaration. Count C function pointers as "non-pointer-containing", since we have no heap-allocated C functions. In runtime, add NOPTR to the remaining pointer-containing declarations, none of which refer to Go heap objects. In runtime, also move os.Args and syscall.envs data into runtime-owned variables. Otherwise, in programs that do not import os or syscall, the runtime variables named os.Args and syscall.envs will be missing type information. I believe that this CL eliminates the final source of conservative GC scanning in non-SWIG Go programs, and therefore... Fixes #909. LGTM=iant R=iant CC=golang-codereviews https://golang.org/cl/149770043 --- src/cmd/cc/dcl.c | 9 ++++++--- src/cmd/cc/lex.c | 2 ++ src/cmd/cgo/out.go | 13 +++++++++++++ src/cmd/dist/buildruntime.c | 2 ++ src/cmd/ld/data.c | 1 + src/liblink/objfile.c | 6 ++++-- src/os/proc.go | 6 ++++++ src/runtime/asm_386.s | 2 -- src/runtime/asm_amd64.s | 2 -- src/runtime/asm_amd64p32.s | 2 -- src/runtime/heapdump.c | 3 +++ src/runtime/malloc.c | 23 ----------------------- src/runtime/malloc.h | 2 -- src/runtime/mcache.c | 2 +- src/runtime/mgc0.c | 5 ++--- src/runtime/os_windows.c | 6 +++--- src/runtime/proc.c | 30 +++++++++--------------------- src/runtime/proc.go | 8 ++++++++ src/runtime/runtime.c | 25 ++++++++++++++++--------- src/runtime/runtime.go | 11 +++++++++++ src/runtime/signals_darwin.h | 3 +++ src/runtime/signals_dragonfly.h | 3 +++ src/runtime/signals_freebsd.h | 3 +++ src/runtime/signals_linux.h | 3 +++ src/runtime/signals_nacl.h | 3 +++ src/runtime/signals_netbsd.h | 3 +++ src/runtime/signals_openbsd.h | 3 +++ src/runtime/signals_plan9.h | 3 +++ src/runtime/signals_solaris.h | 3 +++ src/runtime/stack.c | 4 ++-- src/runtime/thunk.s | 8 +++++++- src/syscall/env_unix.go | 4 +++- 32 files changed, 126 insertions(+), 77 deletions(-) (limited to 'src') diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c index 292717d688..117508fd6d 100644 --- a/src/cmd/cc/dcl.c +++ b/src/cmd/cc/dcl.c @@ -30,6 +30,9 @@ #include #include "cc.h" +#include "../ld/textflag.h" + +static int haspointers(Type*); Node* dodecl(void (*f)(int,Type*,Sym*), int c, Type *t, Node *n) @@ -123,7 +126,8 @@ loop: if(dataflag) { s->dataflag = dataflag; dataflag = 0; - } + } else if(s->type != T && !haspointers(s->type)) + s->dataflag = NOPTR; firstbit = 0; n->sym = s; n->type = s->type; @@ -568,9 +572,8 @@ haspointers(Type *t) return 0; case TARRAY: return haspointers(t->link); - case TFUNC: case TIND: - return 1; + return t->link->etype != TFUNC; default: return 0; } diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c index 55fc36b1e0..7c9f718c09 100644 --- a/src/cmd/cc/lex.c +++ b/src/cmd/cc/lex.c @@ -31,6 +31,7 @@ #include #include "cc.h" #include "y.tab.h" +#include "../ld/textflag.h" #ifndef CPP #define CPP "cpp" @@ -1317,6 +1318,7 @@ cinit(void) t->width = 0; symstring = slookup(".string"); symstring->class = CSTATIC; + symstring->dataflag = NOPTR; symstring->type = t; t = typ(TARRAY, types[TCHAR]); diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 6586531ada..2d14f766fc 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -129,6 +129,7 @@ func (p *Package) writeDefs() { fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle) fmt.Fprintf(&gccgoInit, "\t%s = %s%s;\n", n.Mangle, amp, n.C) } else { + fmt.Fprintf(fc, "#pragma dataflag NOPTR /* C pointer, not heap pointer */ \n") fmt.Fprintf(fc, "void *·%s = %s%s;\n", n.Mangle, amp, n.C) } fmt.Fprintf(fc, "\n") @@ -397,6 +398,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { // C wrapper calls into gcc, passing a pointer to the argument frame. fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", cname) fmt.Fprintf(fc, "void %s(void*);\n", cname) + fmt.Fprintf(fc, "#pragma dataflag NOPTR\n") fmt.Fprintf(fc, "void *·%s = %s;\n", cname, cname) nret := 0 @@ -1151,20 +1153,31 @@ void *_CMalloc(size_t); const cProlog = ` #include "runtime.h" #include "cgocall.h" +#include "textflag.h" +#pragma dataflag NOPTR static void *cgocall_errno = runtime·cgocall_errno; +#pragma dataflag NOPTR void *·_cgo_runtime_cgocall_errno = &cgocall_errno; +#pragma dataflag NOPTR static void *runtime_gostring = runtime·gostring; +#pragma dataflag NOPTR void *·_cgo_runtime_gostring = &runtime_gostring; +#pragma dataflag NOPTR static void *runtime_gostringn = runtime·gostringn; +#pragma dataflag NOPTR void *·_cgo_runtime_gostringn = &runtime_gostringn; +#pragma dataflag NOPTR static void *runtime_gobytes = runtime·gobytes; +#pragma dataflag NOPTR void *·_cgo_runtime_gobytes = &runtime_gobytes; +#pragma dataflag NOPTR static void *runtime_cmalloc = runtime·cmalloc; +#pragma dataflag NOPTR void *·_cgo_runtime_cmalloc = &runtime_cmalloc; void ·_Cerrno(void*, int32); diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c index 1257d5b811..bb774e05fc 100644 --- a/src/cmd/dist/buildruntime.c +++ b/src/cmd/dist/buildruntime.c @@ -330,9 +330,11 @@ mkzsys(char *dir, char *file) static char *runtimedefs[] = { "defs.c", "malloc.c", + "mcache.c", "mgc0.c", "proc.c", "parfor.c", + "stack.c", }; // mkzruntimedefs writes zruntime_defs_$GOOS_$GOARCH.h, diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 71624c3304..9d224d9eb9 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -818,6 +818,7 @@ proggenaddsym(ProgGen *g, LSym *s) if(s->gotype == nil && s->size >= PtrSize) { // conservative scan + diag("missing Go type information for global symbol: %s", s->name); if((s->size%PtrSize) || (g->pos%PtrSize)) diag("proggenaddsym: unaligned conservative symbol %s: size=%lld pos=%lld", s->name, s->size, g->pos); diff --git a/src/liblink/objfile.c b/src/liblink/objfile.c index 9b1e1b7a8f..15d602df92 100644 --- a/src/liblink/objfile.c +++ b/src/liblink/objfile.c @@ -550,7 +550,7 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn) static int ndup; char *name; Reloc *r; - LSym *s, *dup; + LSym *s, *dup, *typ; Pcln *pc; Auto *a; @@ -586,7 +586,9 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn) s->type = t; if(s->size < size) s->size = size; - s->gotype = rdsym(ctxt, f, pkg); + typ = rdsym(ctxt, f, pkg); + if(typ != nil) // if bss sym defined multiple times, take type from any one def + s->gotype = typ; rddata(f, &s->p, &s->np); s->maxp = s->np; n = rdint(f); diff --git a/src/os/proc.go b/src/os/proc.go index 38c436ec54..b63c85ad90 100644 --- a/src/os/proc.go +++ b/src/os/proc.go @@ -11,6 +11,12 @@ import "syscall" // Args hold the command-line arguments, starting with the program name. var Args []string +func init() { + Args = runtime_args() +} + +func runtime_args() []string // in package runtime + // Getuid returns the numeric user id of the caller. func Getuid() int { return syscall.Getuid() } diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index 2961f10f2a..846a214d55 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -903,8 +903,6 @@ TEXT runtime·emptyfunc(SB),0,$0-0 TEXT runtime·abort(SB),NOSPLIT,$0-0 INT $0x3 -GLOBL runtime·tls0(SB), $32 - // hash function using AES hardware instructions TEXT runtime·aeshash(SB),NOSPLIT,$0-16 MOVL p+0(FP), AX // ptr to data diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 44159bb57e..7304d79a2f 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -871,8 +871,6 @@ TEXT runtime·gocputicks(SB),NOSPLIT,$0-8 MOVQ AX, ret+0(FP) RET -GLOBL runtime·tls0(SB), $64 - // hash function using AES hardware instructions TEXT runtime·aeshash(SB),NOSPLIT,$0-32 MOVQ p+0(FP), AX // ptr to data diff --git a/src/runtime/asm_amd64p32.s b/src/runtime/asm_amd64p32.s index bbbd886a53..13a1642568 100644 --- a/src/runtime/asm_amd64p32.s +++ b/src/runtime/asm_amd64p32.s @@ -674,8 +674,6 @@ TEXT runtime·gocputicks(SB),NOSPLIT,$0-8 MOVQ AX, ret+0(FP) RET -GLOBL runtime·tls0(SB), $64 - // hash function using AES hardware instructions // For now, our one amd64p32 system (NaCl) does not // support using AES instructions, so have not bothered to diff --git a/src/runtime/heapdump.c b/src/runtime/heapdump.c index 75897c3d35..54b9666b55 100644 --- a/src/runtime/heapdump.c +++ b/src/runtime/heapdump.c @@ -59,6 +59,8 @@ static BitVector makeheapobjbv(byte *p, uintptr size); // fd to write the dump to. static uintptr dumpfd; + +#pragma dataflag NOPTR /* tmpbuf not a heap pointer at least */ static byte *tmpbuf; static uintptr tmpbufsize; @@ -109,6 +111,7 @@ typedef struct TypeCacheBucket TypeCacheBucket; struct TypeCacheBucket { Type *t[TypeCacheAssoc]; }; +#pragma dataflag NOPTR /* only initialized and used while world is stopped */ static TypeCacheBucket typecache[TypeCacheBuckets]; // dump a uint64 in a varint format parseable by encoding/binary diff --git a/src/runtime/malloc.c b/src/runtime/malloc.c index 60d20a992d..b79c30b720 100644 --- a/src/runtime/malloc.c +++ b/src/runtime/malloc.c @@ -329,29 +329,6 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n) return p; } -// Runtime stubs. - -static void* -cnew(Type *typ, intgo n) -{ - if(n < 0 || (typ->size > 0 && n > MaxMem/typ->size)) - runtime·throw("runtime: allocation size out of range"); - return runtime·mallocgc(typ->size*n, typ, typ->kind&KindNoPointers ? FlagNoScan : 0); -} - -// same as runtime·new, but callable from C -void* -runtime·cnew(Type *typ) -{ - return cnew(typ, 1); -} - -void* -runtime·cnewarray(Type *typ, intgo n) -{ - return cnew(typ, n); -} - void runtime·setFinalizer_m(void) { diff --git a/src/runtime/malloc.h b/src/runtime/malloc.h index 410a007173..b90f1baf29 100644 --- a/src/runtime/malloc.h +++ b/src/runtime/malloc.h @@ -526,8 +526,6 @@ uintptr runtime·sweepone(void); void runtime·markspan(void *v, uintptr size, uintptr n, bool leftover); void runtime·unmarkspan(void *v, uintptr size); void runtime·purgecachedstats(MCache*); -void* runtime·cnew(Type*); -void* runtime·cnewarray(Type*, intgo); void runtime·tracealloc(void*, uintptr, Type*); void runtime·tracefree(void*, uintptr); void runtime·tracegc(void); diff --git a/src/runtime/mcache.c b/src/runtime/mcache.c index 17ea5d2e26..5fdbe32667 100644 --- a/src/runtime/mcache.c +++ b/src/runtime/mcache.c @@ -13,7 +13,7 @@ extern volatile intgo runtime·MemProfileRate; // dummy MSpan that contains no free objects. -static MSpan runtime·emptymspan; +MSpan runtime·emptymspan; MCache* runtime·allocmcache(void) diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c index 54728d5ada..c92fa1db73 100644 --- a/src/runtime/mgc0.c +++ b/src/runtime/mgc0.c @@ -120,7 +120,7 @@ FinBlock* runtime·finc; // cache of free blocks static byte finptrmask[FinBlockSize/PtrSize/PointersPerByte]; bool runtime·fingwait; bool runtime·fingwake; -static FinBlock *runtime·allfin; // list of all blocks +FinBlock *runtime·allfin; // list of all blocks BitVector runtime·gcdatamask; BitVector runtime·gcbssmask; @@ -140,7 +140,7 @@ static BitVector unrollglobgcprog(byte *prog, uintptr size); void runtime·bgsweep(void); static FuncVal bgsweepv = {runtime·bgsweep}; -static struct { +struct { uint64 full; // lock-free list of full blocks uint64 empty; // lock-free list of empty blocks byte pad0[CacheLineSize]; // prevents false-sharing between full/empty and nproc/nwait @@ -1038,7 +1038,6 @@ runtime·MSpan_Sweep(MSpan *s, bool preserve) // State of background runtime·sweep. // Protected by runtime·gclock. -// Must match mgc0.go. struct { G* g; diff --git a/src/runtime/os_windows.c b/src/runtime/os_windows.c index 62d94b65a0..6546d51d33 100644 --- a/src/runtime/os_windows.c +++ b/src/runtime/os_windows.c @@ -147,7 +147,7 @@ runtime·get_random_data(byte **rnd, int32 *rnd_len) void runtime·goenvs(void) { - extern Slice syscall·envs; + extern Slice runtime·envs; uint16 *env; String *s; @@ -160,8 +160,8 @@ runtime·goenvs(void) for(p=env; *p; n++) p += runtime·findnullw(p)+1; - syscall·envs = runtime·makeStringSlice(n); - s = (String*)syscall·envs.array; + runtime·envs = runtime·makeStringSlice(n); + s = (String*)runtime·envs.array; p = env; for(i=0; im->locks++; // disable GC because it can be called from sysmon if(g->m->p == nil) acquirep(p); // temporarily borrow p for mallocs in this function - if(mtype == nil) { - Eface e; - runtime·gc_m_ptr(&e); - mtype = ((PtrType*)e.type)->elem; - } - - mp = runtime·cnew(mtype); + mp = runtime·newM(); mcommoninit(mp); // In case of cgo or Solaris, pthread_create will make us a stack. @@ -889,19 +884,12 @@ runtime·allocm(P *p) return mp; } +G *runtime·newG(void); // in proc.go + static G* allocg(void) { - G *gp; - static Type *gtype; - - if(gtype == nil) { - Eface e; - runtime·gc_g_ptr(&e); - gtype = ((PtrType*)e.type)->elem; - } - gp = runtime·cnew(gtype); - return gp; + return runtime·newG(); } static M* lockextra(bool nilokay); diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 9b95868594..4bb661b54b 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -202,6 +202,14 @@ func newP() *p { return new(p) } +func newM() *m { + return new(m) +} + +func newG() *g { + return new(g) +} + func allgadd(gp *g) { if readgstatus(gp) == _Gidle { gothrow("allgadd: bad status Gidle") diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index aa8dd8f7a0..b3503fb909 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -62,10 +62,12 @@ runtime·mchr(byte *p, byte c, byte *ep) } static int32 argc; + +#pragma dataflag NOPTR /* argv not a heap pointer */ static uint8** argv; -Slice os·Args; -Slice syscall·envs; +extern Slice runtime·argslice; +extern Slice runtime·envs; void (*runtime·sysargs)(int32, uint8**); @@ -97,8 +99,8 @@ runtime·goargs(void) if(Windows) return; - os·Args = runtime·makeStringSlice(argc); - s = (String*)os·Args.array; + runtime·argslice = runtime·makeStringSlice(argc); + s = (String*)runtime·argslice.array; for(i=0; i Date: Wed, 24 Sep 2014 14:33:30 -0700 Subject: fmt: document and fix the handling of precision for strings and byte slices Previous behavior was undocumented and inconsistent. Now it is documented and consistent and measures the input size, since that makes more sense when talking about %q and %x. For %s the change has no effect. Fixes #8151. LGTM=iant R=golang-codereviews, iant CC=golang-codereviews https://golang.org/cl/144540044 --- src/fmt/doc.go | 20 ++++++++++++-------- src/fmt/fmt_test.go | 7 +++++++ src/fmt/format.go | 6 ++++++ 3 files changed, 25 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/fmt/doc.go b/src/fmt/doc.go index b7eaedc11e..00dd8d01cd 100644 --- a/src/fmt/doc.go +++ b/src/fmt/doc.go @@ -63,16 +63,20 @@ %9.2f width 9, precision 2 %9.f width 9, precision 0 - Width and precision are measured in units of Unicode code points. - (This differs from C's printf where the units are numbers - of bytes.) Either or both of the flags may be replaced with the - character '*', causing their values to be obtained from the next - operand, which must be of type int. + Width and precision are measured in units of Unicode code points, + that is, runes. (This differs from C's printf where the + units are always measured in bytes.) Either or both of the flags + may be replaced with the character '*', causing their values to be + obtained from the next operand, which must be of type int. - For most values, width is the minimum number of characters to output, + For most values, width is the minimum number of runes to output, padding the formatted form with spaces if necessary. - For strings, precision is the maximum number of characters to output, - truncating if necessary. + + For strings, byte slices and byte arrays, however, precision + limits the length of the input to be formatted (not the size of + the output), truncating if necessary. Normally it is measured in + runes, but for these types when formatted with the %x or %X format + it is measured in bytes. For floating-point values, width sets the minimum width of the field and precision sets the number of places after the decimal, if appropriate, diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go index cca0a495ff..4586fcf933 100644 --- a/src/fmt/fmt_test.go +++ b/src/fmt/fmt_test.go @@ -194,8 +194,15 @@ var fmtTests = []struct { {"%.5s", "日本語日本語", "日本語日本"}, {"%.5s", []byte("日本語日本語"), "日本語日本"}, {"%.5q", "abcdefghijklmnopqrstuvwxyz", `"abcde"`}, + {"%.5x", "abcdefghijklmnopqrstuvwxyz", `6162636465`}, + {"%.5q", []byte("abcdefghijklmnopqrstuvwxyz"), `"abcde"`}, + {"%.5x", []byte("abcdefghijklmnopqrstuvwxyz"), `6162636465`}, {"%.3q", "日本語日本語", `"日本語"`}, {"%.3q", []byte("日本語日本語"), `"日本語"`}, + {"%.1q", "日本語", `"日"`}, + {"%.1q", []byte("日本語"), `"日"`}, + {"%.1x", "日本語", `e6`}, + {"%.1X", []byte("日本語"), `E6`}, {"%10.1q", "日本語日本語", ` "日"`}, {"%3c", '⌘', " ⌘"}, {"%5q", '\u2026', ` '…'`}, diff --git a/src/fmt/format.go b/src/fmt/format.go index 255167c8f5..a92f3c2f86 100644 --- a/src/fmt/format.go +++ b/src/fmt/format.go @@ -340,11 +340,17 @@ func (f *fmt) fmt_sbx(s string, b []byte, digits string) { // fmt_sx formats a string as a hexadecimal encoding of its bytes. func (f *fmt) fmt_sx(s, digits string) { + if f.precPresent && f.prec < len(s) { + s = s[:f.prec] + } f.fmt_sbx(s, nil, digits) } // fmt_bx formats a byte slice as a hexadecimal encoding of its bytes. func (f *fmt) fmt_bx(b []byte, digits string) { + if f.precPresent && f.prec < len(b) { + b = b[:f.prec] + } f.fmt_sbx("", b, digits) } -- cgit v1.3 From 117a6973cb17d45c0c14cd8fb576f5a06b0d7234 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 14:45:11 -0700 Subject: build: fix elf builds Corrections due to new strict type rules for data+bss. Also disable misc/cgo/cdefstest since you can't compile C code anymore. TBR=iant CC=golang-codereviews https://golang.org/cl/148050044 --- include/link.h | 2 +- src/cmd/ld/data.c | 13 ++++++++++--- src/cmd/ld/lib.c | 4 +++- src/run.bash | 11 +++++++---- src/runtime/vdso_linux_amd64.c | 9 +++++++-- 5 files changed, 28 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/include/link.h b/include/link.h index 292b077394..845f9338d9 100644 --- a/include/link.h +++ b/include/link.h @@ -204,10 +204,10 @@ enum SELFSECT, SMACHO, /* Mach-O __nl_symbol_ptr */ SMACHOGOT, + SWINDOWS, SNOPTRDATA, SINITARR, SDATA, - SWINDOWS, SBSS, SNOPTRBSS, STLSBSS, diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 9d224d9eb9..89226bfe28 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -625,6 +625,7 @@ addstrdata(char *name, char *value) sp = linklookup(ctxt, p, 0); free(p); addstring(sp, value); + sp->type = SRODATA; s = linklookup(ctxt, name, 0); s->size = 0; @@ -816,9 +817,15 @@ proggenaddsym(ProgGen *g, LSym *s) proggenskip(g, g->pos, s->value - g->pos); g->pos += s->value - g->pos; - if(s->gotype == nil && s->size >= PtrSize) { + // The test for names beginning with . here is meant + // to keep .dynamic and .dynsym from turning up as + // conservative symbols. They should be marked SELFSECT + // and not SDATA, but sometimes that doesn't happen. + // Leave debugging the SDATA issue for the Go rewrite. + + if(s->gotype == nil && s->size >= PtrSize && s->name[0] != '.') { // conservative scan - diag("missing Go type information for global symbol: %s", s->name); + diag("missing Go type information for global symbol: %s size %d", s->name, (int)s->size); if((s->size%PtrSize) || (g->pos%PtrSize)) diag("proggenaddsym: unaligned conservative symbol %s: size=%lld pos=%lld", s->name, s->size, g->pos); @@ -834,7 +841,7 @@ proggenaddsym(ProgGen *g, LSym *s) proggenarrayend(g); } g->pos = s->value + size; - } else if(s->gotype == nil || decodetype_noptr(s->gotype) || s->size < PtrSize) { + } else if(s->gotype == nil || decodetype_noptr(s->gotype) || s->size < PtrSize || s->name[0] == '.') { // no scan if(s->size < 32*PtrSize) { // Emit small symbols as data. diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 36f0f99de2..3edf7253d4 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -221,8 +221,10 @@ loadlib(void) // Provided by the code that imports the package. // Since we are simulating the import, we have to provide this string. cgostrsym = "go.string.\"runtime/cgo\""; - if(linkrlookup(ctxt, cgostrsym, 0) == nil) + if(linkrlookup(ctxt, cgostrsym, 0) == nil) { addstrdata(cgostrsym, "runtime/cgo"); + linklookup(ctxt, cgostrsym, 0)->type = SRODATA; + } } if(linkmode == LinkAuto) { diff --git a/src/run.bash b/src/run.bash index 4966cf1aa6..3c9430c87e 100755 --- a/src/run.bash +++ b/src/run.bash @@ -167,10 +167,13 @@ esac # This tests cgo -cdefs. That mode is not supported, # so it's okay if it doesn't work on some systems. # In particular, it works badly with clang on OS X. -[ "$CGO_ENABLED" != 1 ] || [ "$GOOS" == darwin ] || -(xcd ../misc/cgo/testcdefs -./test.bash || exit 1 -) || exit $? +# It doesn't work at all now that we disallow C code +# outside runtime. Once runtime has no C code it won't +# even be necessary. +# [ "$CGO_ENABLED" != 1 ] || [ "$GOOS" == darwin ] || +# (xcd ../misc/cgo/testcdefs +# ./test.bash || exit 1 +# ) || exit $? [ "$CGO_ENABLED" != 1 ] || [ "$GOOS" == darwin ] || (xcd ../misc/cgo/testgodefs diff --git a/src/runtime/vdso_linux_amd64.c b/src/runtime/vdso_linux_amd64.c index 38e1152438..41a41fdd6a 100644 --- a/src/runtime/vdso_linux_amd64.c +++ b/src/runtime/vdso_linux_amd64.c @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "runtime.h" +#include "textflag.h" // Look up symbols in the Linux vDSO. @@ -171,14 +172,18 @@ struct vdso_info { Elf64_Verdef *verdef; }; +#pragma dataflag NOPTR static version_key linux26 = { (byte*)"LINUX_2.6", 0x3ae75f6 }; // initialize with vsyscall fallbacks +#pragma dataflag NOPTR void* runtime·__vdso_time_sym = (void*)0xffffffffff600400ULL; +#pragma dataflag NOPTR void* runtime·__vdso_gettimeofday_sym = (void*)0xffffffffff600000ULL; +#pragma dataflag NOPTR void* runtime·__vdso_clock_gettime_sym = (void*)0; -#define SYM_KEYS_COUNT 3 +#pragma dataflag NOPTR static symbol_key sym_keys[] = { { (byte*)"__vdso_time", 0xa33c485, &runtime·__vdso_time_sym }, { (byte*)"__vdso_gettimeofday", 0x315ca59, &runtime·__vdso_gettimeofday_sym }, @@ -301,7 +306,7 @@ vdso_parse_symbols(struct vdso_info *vdso_info, int32 version) if(vdso_info->valid == false) return; - for(i=0; ibucket[sym_keys[i].sym_hash % vdso_info->nbucket]; chain != 0; chain = vdso_info->chain[chain]) { -- cgit v1.3 From 39cd39b0238b079227460448f298bdd097562e7e Mon Sep 17 00:00:00 2001 From: Dmitriy Vyukov Date: Thu, 25 Sep 2014 01:49:04 +0400 Subject: cmd/go: strip -fsanitize= flags when building cgo object Fixes #8788. LGTM=iant R=iant CC=golang-codereviews https://golang.org/cl/142470043 --- src/cmd/go/build.go | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 2e52731529..27bd307378 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -2228,6 +2228,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfi strings.HasSuffix(f, ".so"), strings.HasSuffix(f, ".dll"): continue + // Remove any -fsanitize=foo flags. + // Otherwise the compiler driver thinks that we are doing final link + // and links sanitizer runtime into the object file. But we are not doing + // the final link, we will link the resulting object file again. And + // so the program ends up with two copies of sanitizer runtime. + // See issue 8788 for details. + case strings.HasPrefix(f, "-fsanitize="): + continue default: bareLDFLAGS = append(bareLDFLAGS, f) } -- cgit v1.3 From 75cca0526dc00ffeacc2aecfa6a0263a5f276e8b Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 17:50:44 -0400 Subject: runtime: more NOPTR Fixes (or makes better) various builds. TBR=iant CC=golang-codereviews https://golang.org/cl/146280043 --- src/runtime/cgo/dragonfly.c | 4 ++++ src/runtime/cgo/freebsd.c | 4 ++++ src/runtime/cgo/netbsd.c | 4 ++++ src/runtime/cgo/openbsd.c | 4 ++++ src/runtime/os_windows.c | 4 ++++ src/runtime/rt0_windows_386.s | 2 +- src/runtime/rt0_windows_amd64.s | 2 +- 7 files changed, 22 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/runtime/cgo/dragonfly.c b/src/runtime/cgo/dragonfly.c index acf53e2655..3c95ff354e 100644 --- a/src/runtime/cgo/dragonfly.c +++ b/src/runtime/cgo/dragonfly.c @@ -2,11 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include "textflag.h" + // Supply environ and __progname, because we don't // link against the standard DragonFly crt0.o and the // libc dynamic library needs them. +#pragma dataflag NOPTR char *environ[1]; +#pragma dataflag NOPTR char *__progname; #pragma dynexport environ environ diff --git a/src/runtime/cgo/freebsd.c b/src/runtime/cgo/freebsd.c index dfcfa3a213..aefc481e64 100644 --- a/src/runtime/cgo/freebsd.c +++ b/src/runtime/cgo/freebsd.c @@ -2,11 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include "textflag.h" + // Supply environ and __progname, because we don't // link against the standard FreeBSD crt0.o and the // libc dynamic library needs them. +#pragma dataflag NOPTR char *environ[1]; +#pragma dataflag NOPTR char *__progname; #pragma dynexport environ environ diff --git a/src/runtime/cgo/netbsd.c b/src/runtime/cgo/netbsd.c index b6403f686c..de38bb7707 100644 --- a/src/runtime/cgo/netbsd.c +++ b/src/runtime/cgo/netbsd.c @@ -2,11 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include "textflag.h" + // Supply environ and __progname, because we don't // link against the standard NetBSD crt0.o and the // libc dynamic library needs them. +#pragma dataflag NOPTR char *environ[1]; +#pragma dataflag NOPTR char *__progname; #pragma dynexport environ environ diff --git a/src/runtime/cgo/openbsd.c b/src/runtime/cgo/openbsd.c index 84e9f9efff..7c2b6c1737 100644 --- a/src/runtime/cgo/openbsd.c +++ b/src/runtime/cgo/openbsd.c @@ -2,11 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include "textflag.h" + // Supply environ, __progname and __guard_local, because // we don't link against the standard OpenBSD crt0.o and // the libc dynamic library needs them. +#pragma dataflag NOPTR char *environ[1]; +#pragma dataflag NOPTR char *__progname; long __guard_local; diff --git a/src/runtime/os_windows.c b/src/runtime/os_windows.c index 6546d51d33..77f99062cf 100644 --- a/src/runtime/os_windows.c +++ b/src/runtime/os_windows.c @@ -72,6 +72,7 @@ extern void *runtime·WaitForSingleObject; extern void *runtime·WriteFile; extern void *runtime·timeBeginPeriod; +#pragma dataflag NOPTR void *runtime·GetQueuedCompletionStatusEx; extern uintptr runtime·externalthreadhandlerp; @@ -289,7 +290,9 @@ typedef struct KSYSTEM_TIME { int32 High2Time; } KSYSTEM_TIME; +#pragma dataflag NOPTR const KSYSTEM_TIME* INTERRUPT_TIME = (KSYSTEM_TIME*)0x7ffe0008; +#pragma dataflag NOPTR const KSYSTEM_TIME* SYSTEM_TIME = (KSYSTEM_TIME*)0x7ffe0014; static void badsystime(void); @@ -500,6 +503,7 @@ runtime·ctrlhandler1(uint32 type) extern void runtime·dosigprof(Context *r, G *gp, M *mp); extern void runtime·profileloop(void); +#pragma dataflag NOPTR static void *profiletimer; static void diff --git a/src/runtime/rt0_windows_386.s b/src/runtime/rt0_windows_386.s index 00604372f1..3c2deda903 100644 --- a/src/runtime/rt0_windows_386.s +++ b/src/runtime/rt0_windows_386.s @@ -17,4 +17,4 @@ TEXT main(SB),NOSPLIT,$0 DATA runtime·iswindows(SB)/4, $1 -GLOBL runtime·iswindows(SB), $4 +GLOBL runtime·iswindows(SB), NOPTR, $4 diff --git a/src/runtime/rt0_windows_amd64.s b/src/runtime/rt0_windows_amd64.s index 890a570d1d..197f52e113 100644 --- a/src/runtime/rt0_windows_amd64.s +++ b/src/runtime/rt0_windows_amd64.s @@ -16,4 +16,4 @@ TEXT main(SB),NOSPLIT,$-8 JMP AX DATA runtime·iswindows(SB)/4, $1 -GLOBL runtime·iswindows(SB), $4 +GLOBL runtime·iswindows(SB), NOPTR, $4 -- cgit v1.3 From 665a4166650d088c067130eb41f8f95efb9c12ed Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 24 Sep 2014 18:50:54 -0400 Subject: os: fix Args setup on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Should fix the Windows build. Untested. on Windows, args are made by src/os/exec_windows.go, not package runtime. runtime·goargs has if(Windows) return; The two init funcs in pkg os were conflicting, with the second overwriting Args back to an empty slice. LGTM=rsc R=rsc CC=golang-codereviews https://golang.org/cl/143540044 --- src/os/proc.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/os/proc.go b/src/os/proc.go index b63c85ad90..774f09900e 100644 --- a/src/os/proc.go +++ b/src/os/proc.go @@ -6,12 +6,19 @@ package os -import "syscall" +import ( + "runtime" + "syscall" +) // Args hold the command-line arguments, starting with the program name. var Args []string func init() { + if runtime.GOOS == "windows" { + // Initialized in exec_windows.go. + return + } Args = runtime_args() } -- cgit v1.3 From 3c94b1d305967d260b31ec3fdda51b705db752cd Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 19:04:06 -0400 Subject: runtime: more NOPTR Fixes linux builds (_vdso); may fix others. I can at least cross-compile cmd/go for every implemented system now. TBR=iant CC=golang-codereviews https://golang.org/cl/142630043 --- src/runtime/mem_plan9.c | 1 + src/runtime/rt0_linux_386.s | 2 +- src/runtime/rt0_linux_arm.s | 2 +- src/runtime/rt0_plan9_386.s | 8 ++++---- src/runtime/rt0_plan9_amd64.s | 8 ++++---- src/runtime/rt0_solaris_amd64.s | 2 +- src/runtime/sys_dragonfly_386.s | 2 +- src/runtime/sys_freebsd_386.s | 2 +- src/runtime/sys_netbsd_386.s | 2 +- src/runtime/sys_openbsd_386.s | 2 +- src/runtime/sys_windows_386.s | 2 +- src/runtime/sys_windows_amd64.s | 2 +- src/sync/atomic/asm_arm.s | 2 +- src/sync/atomic/asm_linux_arm.s | 2 +- 14 files changed, 20 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/runtime/mem_plan9.c b/src/runtime/mem_plan9.c index 402869f393..d673d6f830 100644 --- a/src/runtime/mem_plan9.c +++ b/src/runtime/mem_plan9.c @@ -10,6 +10,7 @@ #include "textflag.h" extern byte runtime·end[]; +#pragma dataflag NOPTR static byte *bloc = { runtime·end }; static Mutex memlock; diff --git a/src/runtime/rt0_linux_386.s b/src/runtime/rt0_linux_386.s index 74ddc94da9..352e594d53 100644 --- a/src/runtime/rt0_linux_386.s +++ b/src/runtime/rt0_linux_386.s @@ -21,5 +21,5 @@ TEXT _fallback_vdso(SB),NOSPLIT,$0 RET DATA runtime·_vdso(SB)/4, $_fallback_vdso(SB) -GLOBL runtime·_vdso(SB), $4 +GLOBL runtime·_vdso(SB), NOPTR, $4 diff --git a/src/runtime/rt0_linux_arm.s b/src/runtime/rt0_linux_arm.s index 8af3d3505e..5f521d24ba 100644 --- a/src/runtime/rt0_linux_arm.s +++ b/src/runtime/rt0_linux_arm.s @@ -77,7 +77,7 @@ DATA bad_abi_msg+0x18(SB)/8, $" run on " DATA bad_abi_msg+0x20(SB)/8, $"EABI ker" DATA bad_abi_msg+0x28(SB)/4, $"nels" DATA bad_abi_msg+0x2c(SB)/1, $0xa -GLOBL bad_abi_msg(SB), $45 +GLOBL bad_abi_msg(SB), RODATA, $45 TEXT oabi_syscall<>(SB),NOSPLIT,$-4 ADD $1, PC, R4 diff --git a/src/runtime/rt0_plan9_386.s b/src/runtime/rt0_plan9_386.s index 7e2887b857..c451299eec 100644 --- a/src/runtime/rt0_plan9_386.s +++ b/src/runtime/rt0_plan9_386.s @@ -17,7 +17,7 @@ TEXT _rt0_386_plan9(SB),NOSPLIT,$12 CALL runtime·rt0_go(SB) DATA runtime·isplan9(SB)/4, $1 -GLOBL runtime·isplan9(SB), $4 -GLOBL _tos(SB), $4 -GLOBL _privates(SB), $4 -GLOBL _nprivates(SB), $4 +GLOBL runtime·isplan9(SB), NOPTR, $4 +GLOBL _tos(SB), NOPTR, $4 +GLOBL _privates(SB), NOPTR, $4 +GLOBL _nprivates(SB), NOPTR, $4 diff --git a/src/runtime/rt0_plan9_amd64.s b/src/runtime/rt0_plan9_amd64.s index a372a0ba8b..ec2d9ec827 100644 --- a/src/runtime/rt0_plan9_amd64.s +++ b/src/runtime/rt0_plan9_amd64.s @@ -15,7 +15,7 @@ TEXT _rt0_amd64_plan9(SB),NOSPLIT,$24 JMP AX DATA runtime·isplan9(SB)/4, $1 -GLOBL runtime·isplan9(SB), $4 -GLOBL _tos(SB), $8 -GLOBL _privates(SB), $8 -GLOBL _nprivates(SB), $4 +GLOBL runtime·isplan9(SB), NOPTR, $4 +GLOBL _tos(SB), NOPTR, $8 +GLOBL _privates(SB), NOPTR, $8 +GLOBL _nprivates(SB), NOPTR, $4 diff --git a/src/runtime/rt0_solaris_amd64.s b/src/runtime/rt0_solaris_amd64.s index 92a9fc2952..5997cbf8e3 100644 --- a/src/runtime/rt0_solaris_amd64.s +++ b/src/runtime/rt0_solaris_amd64.s @@ -15,4 +15,4 @@ TEXT main(SB),NOSPLIT,$-8 JMP AX DATA runtime·issolaris(SB)/4, $1 -GLOBL runtime·issolaris(SB), $4 +GLOBL runtime·issolaris(SB), NOPTR, $4 diff --git a/src/runtime/sys_dragonfly_386.s b/src/runtime/sys_dragonfly_386.s index dd0e27e26a..161eaec19d 100644 --- a/src/runtime/sys_dragonfly_386.s +++ b/src/runtime/sys_dragonfly_386.s @@ -378,4 +378,4 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$32 NEGL AX RET -GLOBL runtime·tlsoffset(SB),$4 +GLOBL runtime·tlsoffset(SB),NOPTR,$4 diff --git a/src/runtime/sys_freebsd_386.s b/src/runtime/sys_freebsd_386.s index ffc28560ec..2c40fc433b 100644 --- a/src/runtime/sys_freebsd_386.s +++ b/src/runtime/sys_freebsd_386.s @@ -388,4 +388,4 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$32 NEGL AX RET -GLOBL runtime·tlsoffset(SB),$4 +GLOBL runtime·tlsoffset(SB),NOPTR,$4 diff --git a/src/runtime/sys_netbsd_386.s b/src/runtime/sys_netbsd_386.s index 83a76cb343..23f2f6bd14 100644 --- a/src/runtime/sys_netbsd_386.s +++ b/src/runtime/sys_netbsd_386.s @@ -350,7 +350,7 @@ TEXT runtime·sysctl(SB),NOSPLIT,$28 MOVL $0, AX RET -GLOBL runtime·tlsoffset(SB),$4 +GLOBL runtime·tlsoffset(SB),NOPTR,$4 // int32 runtime·kqueue(void) TEXT runtime·kqueue(SB),NOSPLIT,$0 diff --git a/src/runtime/sys_openbsd_386.s b/src/runtime/sys_openbsd_386.s index 12d9c5c6b4..5cda7768ae 100644 --- a/src/runtime/sys_openbsd_386.s +++ b/src/runtime/sys_openbsd_386.s @@ -395,4 +395,4 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$32 NEGL AX RET -GLOBL runtime·tlsoffset(SB),$4 +GLOBL runtime·tlsoffset(SB),NOPTR,$4 diff --git a/src/runtime/sys_windows_386.s b/src/runtime/sys_windows_386.s index 9b1fc7a205..1bf4d062ac 100644 --- a/src/runtime/sys_windows_386.s +++ b/src/runtime/sys_windows_386.s @@ -212,7 +212,7 @@ TEXT runtime·externalthreadhandler(SB),NOSPLIT,$0 POPL BP RET -GLOBL runtime·cbctxts(SB), $4 +GLOBL runtime·cbctxts(SB), NOPTR, $4 TEXT runtime·callbackasm1+0(SB),NOSPLIT,$0 MOVL 0(SP), AX // will use to find our callback context diff --git a/src/runtime/sys_windows_amd64.s b/src/runtime/sys_windows_amd64.s index f701d157ed..05750398ea 100644 --- a/src/runtime/sys_windows_amd64.s +++ b/src/runtime/sys_windows_amd64.s @@ -249,7 +249,7 @@ TEXT runtime·externalthreadhandler(SB),NOSPLIT,$0 POPQ BP RET -GLOBL runtime·cbctxts(SB), $8 +GLOBL runtime·cbctxts(SB), NOPTR, $8 TEXT runtime·callbackasm1(SB),NOSPLIT,$0 // Construct args vector for cgocallback(). diff --git a/src/sync/atomic/asm_arm.s b/src/sync/atomic/asm_arm.s index 47639a80ea..8a85273da2 100644 --- a/src/sync/atomic/asm_arm.s +++ b/src/sync/atomic/asm_arm.s @@ -194,4 +194,4 @@ TEXT slowCheck64<>(SB),NOSPLIT,$0-0 MOVW R0, ok64<>(SB) RET -GLOBL ok64<>(SB), $4 +GLOBL ok64<>(SB), NOPTR, $4 diff --git a/src/sync/atomic/asm_linux_arm.s b/src/sync/atomic/asm_linux_arm.s index 63f1f9e38e..944758441a 100644 --- a/src/sync/atomic/asm_linux_arm.s +++ b/src/sync/atomic/asm_linux_arm.s @@ -124,7 +124,7 @@ TEXT kernelCAS64<>(SB),NOSPLIT,$0-21 TEXT ·generalCAS64(SB),NOSPLIT,$0-21 B runtime·cas64(SB) -GLOBL armCAS64(SB), $4 +GLOBL armCAS64(SB), NOPTR, $4 TEXT setupAndCallCAS64<>(SB),NOSPLIT,$-4-21 MOVW $0xffff0ffc, R0 // __kuser_helper_version -- cgit v1.3 From d2b84dd941456751cd09363a1027746683818f09 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 19:09:43 -0400 Subject: net: only "build" empty.s in non-cgo mode In cgo mode it gets passed to gcc, and on ARM it appears that gcc does not support // comments. TBR=iant CC=golang-codereviews https://golang.org/cl/142640043 --- src/net/empty.s | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/net/empty.s b/src/net/empty.s index a515c2fe29..f0b255a0cf 100644 --- a/src/net/empty.s +++ b/src/net/empty.s @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !cgo + // This file is required to prevent compiler errors // when the package built with CGO_ENABLED=0. // Otherwise the compiler says: -- cgit v1.3 From 6077f0fc32a401f9a7b9540b6d1b00e855018c9a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 19:18:01 -0400 Subject: cmd/go: fix bytes and net the right way Not sure why they used empty.s and all these other packages were special cased in cmd/go instead. Add them to the list. This avoids problems with net .s files being compiled with gcc in cgo mode and gcc not supporting // comments on ARM. Not a problem with bytes, but be consistent. The last change fixed the ARM build but broke the Windows build. Maybe *this* will make everyone happy. Sigh. TBR=iant CC=golang-codereviews https://golang.org/cl/144530046 --- src/bytes/bytes.s | 5 ----- src/cmd/go/build.go | 2 +- src/net/empty.s | 10 ---------- 3 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 src/bytes/bytes.s delete mode 100644 src/net/empty.s (limited to 'src') diff --git a/src/bytes/bytes.s b/src/bytes/bytes.s deleted file mode 100644 index 55103bae05..0000000000 --- a/src/bytes/bytes.s +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2013 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. - -// This file is here just to make the go tool happy. diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 27bd307378..fcc6b699be 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -1630,7 +1630,7 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, importArgs [] extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) if p.Standard { switch p.ImportPath { - case "os", "runtime/pprof", "sync", "time": + case "bytes", "net", "os", "runtime/pprof", "sync", "time": extFiles++ } } diff --git a/src/net/empty.s b/src/net/empty.s deleted file mode 100644 index f0b255a0cf..0000000000 --- a/src/net/empty.s +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !cgo - -// This file is required to prevent compiler errors -// when the package built with CGO_ENABLED=0. -// Otherwise the compiler says: -// pkg/net/fd_poll_runtime.go:15: missing function body -- cgit v1.3 From e6f21be3f48802f18013a7e95bb3850882ab96e3 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 24 Sep 2014 16:55:39 -0700 Subject: net/http: support https_proxy in ProxyFromEnvironment Fixes #6181 LGTM=adg R=adg CC=golang-codereviews https://golang.org/cl/148980043 --- src/net/http/export_test.go | 1 + src/net/http/transport.go | 11 ++++++++++- src/net/http/transport_test.go | 33 ++++++++++++++++++++++++++++----- 3 files changed, 39 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/net/http/export_test.go b/src/net/http/export_test.go index 2c87353554..f8cc835b25 100644 --- a/src/net/http/export_test.go +++ b/src/net/http/export_test.go @@ -66,6 +66,7 @@ func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler { func ResetCachedEnvironment() { httpProxyEnv.reset() + httpsProxyEnv.reset() noProxyEnv.reset() } diff --git a/src/net/http/transport.go b/src/net/http/transport.go index f1aab8587c..6be341faa9 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -124,7 +124,13 @@ type Transport struct { // As a special case, if req.URL.Host is "localhost" (with or without // a port number), then a nil URL and nil error will be returned. func ProxyFromEnvironment(req *Request) (*url.URL, error) { - proxy := httpProxyEnv.Get() + var proxy string + if req.URL.Scheme == "https" { + proxy = httpsProxyEnv.Get() + } + if proxy == "" { + proxy = httpProxyEnv.Get() + } if proxy == "" { return nil, nil } @@ -276,6 +282,9 @@ var ( httpProxyEnv = &envOnce{ names: []string{"HTTP_PROXY", "http_proxy"}, } + httpsProxyEnv = &envOnce{ + names: []string{"HTTPS_PROXY", "https_proxy"}, + } noProxyEnv = &envOnce{ names: []string{"NO_PROXY", "no_proxy"}, } diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index bdfeba3626..2ffd359794 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -1701,26 +1701,40 @@ Content-Length: %d } type proxyFromEnvTest struct { - req string // URL to fetch; blank means "http://example.com" - env string - noenv string + req string // URL to fetch; blank means "http://example.com" + + env string // HTTP_PROXY + httpsenv string // HTTPS_PROXY + noenv string // NO_RPXY + want string wanterr error } func (t proxyFromEnvTest) String() string { var buf bytes.Buffer + space := func() { + if buf.Len() > 0 { + buf.WriteByte(' ') + } + } if t.env != "" { fmt.Fprintf(&buf, "http_proxy=%q", t.env) } + if t.httpsenv != "" { + space() + fmt.Fprintf(&buf, "https_proxy=%q", t.httpsenv) + } if t.noenv != "" { - fmt.Fprintf(&buf, " no_proxy=%q", t.noenv) + space() + fmt.Fprintf(&buf, "no_proxy=%q", t.noenv) } req := "http://example.com" if t.req != "" { req = t.req } - fmt.Fprintf(&buf, " req=%q", req) + space() + fmt.Fprintf(&buf, "req=%q", req) return strings.TrimSpace(buf.String()) } @@ -1731,7 +1745,15 @@ var proxyFromEnvTests = []proxyFromEnvTest{ {env: "https://cache.corp.example.com", want: "https://cache.corp.example.com"}, {env: "http://127.0.0.1:8080", want: "http://127.0.0.1:8080"}, {env: "https://127.0.0.1:8080", want: "https://127.0.0.1:8080"}, + + // Don't use secure for http + {req: "http://insecure.tld/", env: "http.proxy.tld", httpsenv: "secure.proxy.tld", want: "http://http.proxy.tld"}, + // Use secure for https. + {req: "https://secure.tld/", env: "http.proxy.tld", httpsenv: "secure.proxy.tld", want: "http://secure.proxy.tld"}, + {req: "https://secure.tld/", env: "http.proxy.tld", httpsenv: "https://secure.proxy.tld", want: "https://secure.proxy.tld"}, + {want: ""}, + {noenv: "example.com", req: "http://example.com/", env: "proxy", want: ""}, {noenv: ".example.com", req: "http://example.com/", env: "proxy", want: ""}, {noenv: "ample.com", req: "http://example.com/", env: "proxy", want: "http://proxy"}, @@ -1743,6 +1765,7 @@ func TestProxyFromEnvironment(t *testing.T) { ResetProxyEnv() for _, tt := range proxyFromEnvTests { os.Setenv("HTTP_PROXY", tt.env) + os.Setenv("HTTPS_PROXY", tt.httpsenv) os.Setenv("NO_PROXY", tt.noenv) ResetCachedEnvironment() reqURL := tt.req -- cgit v1.3 From 446524269ee152b8053d44117887bc3cc8d5ef9d Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 24 Sep 2014 17:01:54 -0700 Subject: net/http: check for CloseWrite interface, not TCPConn implementation Fixes #8724 LGTM=adg R=adg CC=golang-codereviews https://golang.org/cl/148040043 --- src/net/http/export_test.go | 4 ++++ src/net/http/serve_test.go | 23 +++++++++++++++++++++++ src/net/http/server.go | 10 ++++++++-- 3 files changed, 35 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/net/http/export_test.go b/src/net/http/export_test.go index f8cc835b25..e5bc02afa2 100644 --- a/src/net/http/export_test.go +++ b/src/net/http/export_test.go @@ -77,3 +77,7 @@ var DefaultUserAgent = defaultUserAgent func SetPendingDialHooks(before, after func()) { prePendingDial, postPendingDial = before, after } + +var ExportServerNewConn = (*Server).newConn + +var ExportCloseWriteAndWait = (*conn).closeWriteAndWait diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go index ee4f204995..a690ae4699 100644 --- a/src/net/http/serve_test.go +++ b/src/net/http/serve_test.go @@ -2607,6 +2607,29 @@ func TestServerConnStateNew(t *testing.T) { } } +type closeWriteTestConn struct { + rwTestConn + didCloseWrite bool +} + +func (c *closeWriteTestConn) CloseWrite() error { + c.didCloseWrite = true + return nil +} + +func TestCloseWrite(t *testing.T) { + var srv Server + var testConn closeWriteTestConn + c, err := ExportServerNewConn(&srv, &testConn) + if err != nil { + t.Fatal(err) + } + ExportCloseWriteAndWait(c) + if !testConn.didCloseWrite { + t.Error("didn't see CloseWrite call") + } +} + func BenchmarkClientServer(b *testing.B) { b.ReportAllocs() b.StopTimer() diff --git a/src/net/http/server.go b/src/net/http/server.go index 8f2b777b29..7ad0bcbc20 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -1064,15 +1064,21 @@ func (c *conn) close() { // This timeout is somewhat arbitrary (~latency around the planet). const rstAvoidanceDelay = 500 * time.Millisecond +type closeWriter interface { + CloseWrite() error +} + +var _ closeWriter = (*net.TCPConn)(nil) + // closeWrite flushes any outstanding data and sends a FIN packet (if // client is connected via TCP), signalling that we're done. We then -// pause for a bit, hoping the client processes it before `any +// pause for a bit, hoping the client processes it before any // subsequent RST. // // See http://golang.org/issue/3595 func (c *conn) closeWriteAndWait() { c.finalFlush() - if tcp, ok := c.rwc.(*net.TCPConn); ok { + if tcp, ok := c.rwc.(closeWriter); ok { tcp.CloseWrite() } time.Sleep(rstAvoidanceDelay) -- cgit v1.3 From e59ad69a44df7f00c5afab3716374f80d1bb47c7 Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Thu, 25 Sep 2014 10:21:52 +1000 Subject: net/http: allow double-quotes only on cookie values, not cookie attribute values, a la RFC 6265 section 4.1.1 "Syntax". Fixes #7751. LGTM=dr.volker.dobler R=dr.volker.dobler CC=bradfitz, golang-codereviews https://golang.org/cl/148890043 --- src/net/http/cookie.go | 10 +++++----- src/net/http/cookie_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/net/http/cookie.go b/src/net/http/cookie.go index dc60ba87f5..a0d0fdbbd0 100644 --- a/src/net/http/cookie.go +++ b/src/net/http/cookie.go @@ -56,7 +56,7 @@ func readSetCookies(h Header) []*Cookie { if !isCookieNameValid(name) { continue } - value, success := parseCookieValue(value) + value, success := parseCookieValue(value, true) if !success { continue } @@ -76,7 +76,7 @@ func readSetCookies(h Header) []*Cookie { attr, val = attr[:j], attr[j+1:] } lowerAttr := strings.ToLower(attr) - val, success = parseCookieValue(val) + val, success = parseCookieValue(val, false) if !success { c.Unparsed = append(c.Unparsed, parts[i]) continue @@ -205,7 +205,7 @@ func readCookies(h Header, filter string) []*Cookie { if filter != "" && filter != name { continue } - val, success := parseCookieValue(val) + val, success := parseCookieValue(val, true) if !success { continue } @@ -345,9 +345,9 @@ func sanitizeOrWarn(fieldName string, valid func(byte) bool, v string) string { return string(buf) } -func parseCookieValue(raw string) (string, bool) { +func parseCookieValue(raw string, allowDoubleQuote bool) (string, bool) { // Strip the quotes, if present. - if len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' { + if allowDoubleQuote && len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' { raw = raw[1 : len(raw)-1] } for i := 0; i < len(raw); i++ { diff --git a/src/net/http/cookie_test.go b/src/net/http/cookie_test.go index f78f37299f..98dc2fade0 100644 --- a/src/net/http/cookie_test.go +++ b/src/net/http/cookie_test.go @@ -313,6 +313,14 @@ var readCookiesTests = []struct { {Name: "c2", Value: "v2"}, }, }, + { + Header{"Cookie": {`Cookie-1="v$1"; c2="v2"`}}, + "", + []*Cookie{ + {Name: "Cookie-1", Value: "v$1"}, + {Name: "c2", Value: "v2"}, + }, + }, } func TestReadCookies(t *testing.T) { @@ -327,6 +335,30 @@ func TestReadCookies(t *testing.T) { } } +func TestSetCookieDoubleQuotes(t *testing.T) { + res := &Response{Header: Header{}} + res.Header.Add("Set-Cookie", `quoted0=none; max-age=30`) + res.Header.Add("Set-Cookie", `quoted1="cookieValue"; max-age=31`) + res.Header.Add("Set-Cookie", `quoted2=cookieAV; max-age="32"`) + res.Header.Add("Set-Cookie", `quoted3="both"; max-age="33"`) + got := res.Cookies() + want := []*Cookie{ + {Name: "quoted0", Value: "none", MaxAge: 30}, + {Name: "quoted1", Value: "cookieValue", MaxAge: 31}, + {Name: "quoted2", Value: "cookieAV"}, + {Name: "quoted3", Value: "both"}, + } + if len(got) != len(want) { + t.Fatal("got %d cookies, want %d", len(got), len(want)) + } + for i, w := range want { + g := got[i] + if g.Name != w.Name || g.Value != w.Value || g.MaxAge != w.MaxAge { + t.Errorf("cookie #%d:\ngot %v\nwant %v", i, g, w) + } + } +} + func TestCookieSanitizeValue(t *testing.T) { defer log.SetOutput(os.Stderr) var logbuf bytes.Buffer -- cgit v1.3 From dfaf1f71e67de2807e07c880060b457d32a66b8b Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 24 Sep 2014 17:39:00 -0700 Subject: net/http: update ProxyFromEnvironment docs for HTTPS_PROXY addition LGTM=adg R=adg CC=golang-codereviews https://golang.org/cl/142650043 --- src/net/http/transport.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/net/http/transport.go b/src/net/http/transport.go index 6be341faa9..f1a6837527 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -116,10 +116,17 @@ type Transport struct { // ProxyFromEnvironment returns the URL of the proxy to use for a // given request, as indicated by the environment variables -// $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy). -// An error is returned if the proxy environment is invalid. +// HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the lowercase versions +// thereof). HTTPS_PROXY takes precedence over HTTP_PROXY for https +// requests. +// +// The environment values may be either a complete URL or a +// "host[:port]", in which case the "http" scheme is assumed. +// An error is returned if the value is a different form. +// // A nil URL and nil error are returned if no proxy is defined in the -// environment, or a proxy should not be used for the given request. +// environment, or a proxy should not be used for the given request, +// as defined by NO_PROXY. // // As a special case, if req.URL.Host is "localhost" (with or without // a port number), then a nil URL and nil error will be returned. -- cgit v1.3 From 1b6807bb069c528447270c3d6c66c5c7597f388f Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 25 Sep 2014 07:59:01 -0700 Subject: cgo: adjust return value location to account for stack copies. During a cgo call, the stack can be copied. This copy invalidates the pointer that cgo has into the return value area. To fix this problem, pass the address of the location containing the stack top value (which is in the G struct). For cgo functions which return values, read the stktop before and after the cgo call to compute the adjustment necessary to write the return value. Fixes #8771 LGTM=iant, rsc R=iant, rsc, khr CC=golang-codereviews https://golang.org/cl/144130043 --- misc/cgo/test/callback.go | 45 +++++++++++++++++++++ misc/cgo/test/callback_c.c | 16 ++++++++ misc/cgo/test/cgo_test.go | 98 +++++++++++++++++++++++---------------------- src/cmd/cgo/out.go | 16 +++++++- src/runtime/asm_386.s | 10 +++++ src/runtime/asm_amd64.s | 11 +++++ src/runtime/asm_arm.s | 8 ++++ src/runtime/cgo/callbacks.c | 3 ++ src/runtime/stack.c | 4 +- 9 files changed, 161 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/misc/cgo/test/callback.go b/misc/cgo/test/callback.go index a7f1a3ecd6..44167e6e9e 100644 --- a/misc/cgo/test/callback.go +++ b/misc/cgo/test/callback.go @@ -10,6 +10,9 @@ void callGoFoo(void); void callGoStackCheck(void); void callPanic(void); void callCgoAllocate(void); +int callGoReturnVal(void); +int returnAfterGrow(void); +int returnAfterGrowFromGo(void); */ import "C" @@ -212,6 +215,48 @@ func testAllocateFromC(t *testing.T) { C.callCgoAllocate() // crashes or exits on failure } +// Test that C code can return a value if it calls a Go function that +// causes a stack copy. +func testReturnAfterGrow(t *testing.T) { + // Use a new goroutine so that we get a small stack. + c := make(chan int) + go func() { + c <- int(C.returnAfterGrow()) + }() + if got, want := <-c, 123456; got != want { + t.Errorf("got %d want %d", got, want) + } +} + +// Test that we can return a value from Go->C->Go if the Go code +// causes a stack copy. +func testReturnAfterGrowFromGo(t *testing.T) { + // Use a new goroutine so that we get a small stack. + c := make(chan int) + go func() { + c <- int(C.returnAfterGrowFromGo()) + }() + if got, want := <-c, 129*128/2; got != want { + t.Errorf("got %d want %d", got, want) + } +} + +//export goReturnVal +func goReturnVal() (r C.int) { + // Force a stack copy. + var f func(int) int + f = func(i int) int { + var buf [256]byte + use(buf[:]) + if i == 0 { + return 0 + } + return i + f(i-1) + } + r = C.int(f(128)) + return +} + func testCallbackStack(t *testing.T) { // Make cgo call and callback with different amount of stack stack available. // We do not do any explicit checks, just ensure that it does not crash. diff --git a/misc/cgo/test/callback_c.c b/misc/cgo/test/callback_c.c index dcd4ddd4ee..5bb6425340 100644 --- a/misc/cgo/test/callback_c.c +++ b/misc/cgo/test/callback_c.c @@ -64,3 +64,19 @@ callGoStackCheck(void) extern void goStackCheck(void); goStackCheck(); } + +int +returnAfterGrow(void) +{ + extern int goReturnVal(void); + goReturnVal(); + return 123456; +} + +int +returnAfterGrowFromGo(void) +{ + extern int goReturnVal(void); + return goReturnVal(); +} + diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go index 1d1abf7291..fcfad83049 100644 --- a/misc/cgo/test/cgo_test.go +++ b/misc/cgo/test/cgo_test.go @@ -10,53 +10,55 @@ import "testing" // so that they can use cgo (import "C"). // These wrappers are here for gotest to find. -func TestAlign(t *testing.T) { testAlign(t) } -func TestConst(t *testing.T) { testConst(t) } -func TestEnum(t *testing.T) { testEnum(t) } -func TestAtol(t *testing.T) { testAtol(t) } -func TestErrno(t *testing.T) { testErrno(t) } -func TestMultipleAssign(t *testing.T) { testMultipleAssign(t) } -func TestUnsignedInt(t *testing.T) { testUnsignedInt(t) } -func TestCallback(t *testing.T) { testCallback(t) } -func TestCallbackGC(t *testing.T) { testCallbackGC(t) } -func TestCallbackPanic(t *testing.T) { testCallbackPanic(t) } -func TestCallbackPanicLoop(t *testing.T) { testCallbackPanicLoop(t) } -func TestCallbackPanicLocked(t *testing.T) { testCallbackPanicLocked(t) } -func TestPanicFromC(t *testing.T) { testPanicFromC(t) } -func TestAllocateFromC(t *testing.T) { testAllocateFromC(t) } -func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) } -func TestBlocking(t *testing.T) { testBlocking(t) } -func Test1328(t *testing.T) { test1328(t) } -func TestParallelSleep(t *testing.T) { testParallelSleep(t) } -func TestSetEnv(t *testing.T) { testSetEnv(t) } -func TestHelpers(t *testing.T) { testHelpers(t) } -func TestLibgcc(t *testing.T) { testLibgcc(t) } -func Test1635(t *testing.T) { test1635(t) } -func TestPrintf(t *testing.T) { testPrintf(t) } -func Test4029(t *testing.T) { test4029(t) } -func TestBoolAlign(t *testing.T) { testBoolAlign(t) } -func Test3729(t *testing.T) { test3729(t) } -func Test3775(t *testing.T) { test3775(t) } -func TestCthread(t *testing.T) { testCthread(t) } -func TestCallbackCallers(t *testing.T) { testCallbackCallers(t) } -func Test5227(t *testing.T) { test5227(t) } -func TestCflags(t *testing.T) { testCflags(t) } -func Test5337(t *testing.T) { test5337(t) } -func Test5548(t *testing.T) { test5548(t) } -func Test5603(t *testing.T) { test5603(t) } -func Test6833(t *testing.T) { test6833(t) } -func Test3250(t *testing.T) { test3250(t) } -func TestCallbackStack(t *testing.T) { testCallbackStack(t) } -func TestFpVar(t *testing.T) { testFpVar(t) } -func Test4339(t *testing.T) { test4339(t) } -func Test6390(t *testing.T) { test6390(t) } -func Test5986(t *testing.T) { test5986(t) } -func Test7665(t *testing.T) { test7665(t) } -func TestNaming(t *testing.T) { testNaming(t) } -func Test7560(t *testing.T) { test7560(t) } -func Test5242(t *testing.T) { test5242(t) } -func Test8092(t *testing.T) { test8092(t) } -func Test7978(t *testing.T) { test7978(t) } -func Test8694(t *testing.T) { test8694(t) } +func TestAlign(t *testing.T) { testAlign(t) } +func TestConst(t *testing.T) { testConst(t) } +func TestEnum(t *testing.T) { testEnum(t) } +func TestAtol(t *testing.T) { testAtol(t) } +func TestErrno(t *testing.T) { testErrno(t) } +func TestMultipleAssign(t *testing.T) { testMultipleAssign(t) } +func TestUnsignedInt(t *testing.T) { testUnsignedInt(t) } +func TestCallback(t *testing.T) { testCallback(t) } +func TestCallbackGC(t *testing.T) { testCallbackGC(t) } +func TestCallbackPanic(t *testing.T) { testCallbackPanic(t) } +func TestCallbackPanicLoop(t *testing.T) { testCallbackPanicLoop(t) } +func TestCallbackPanicLocked(t *testing.T) { testCallbackPanicLocked(t) } +func TestPanicFromC(t *testing.T) { testPanicFromC(t) } +func TestAllocateFromC(t *testing.T) { testAllocateFromC(t) } +func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) } +func TestBlocking(t *testing.T) { testBlocking(t) } +func Test1328(t *testing.T) { test1328(t) } +func TestParallelSleep(t *testing.T) { testParallelSleep(t) } +func TestSetEnv(t *testing.T) { testSetEnv(t) } +func TestHelpers(t *testing.T) { testHelpers(t) } +func TestLibgcc(t *testing.T) { testLibgcc(t) } +func Test1635(t *testing.T) { test1635(t) } +func TestPrintf(t *testing.T) { testPrintf(t) } +func Test4029(t *testing.T) { test4029(t) } +func TestBoolAlign(t *testing.T) { testBoolAlign(t) } +func Test3729(t *testing.T) { test3729(t) } +func Test3775(t *testing.T) { test3775(t) } +func TestCthread(t *testing.T) { testCthread(t) } +func TestCallbackCallers(t *testing.T) { testCallbackCallers(t) } +func Test5227(t *testing.T) { test5227(t) } +func TestCflags(t *testing.T) { testCflags(t) } +func Test5337(t *testing.T) { test5337(t) } +func Test5548(t *testing.T) { test5548(t) } +func Test5603(t *testing.T) { test5603(t) } +func Test6833(t *testing.T) { test6833(t) } +func Test3250(t *testing.T) { test3250(t) } +func TestCallbackStack(t *testing.T) { testCallbackStack(t) } +func TestFpVar(t *testing.T) { testFpVar(t) } +func Test4339(t *testing.T) { test4339(t) } +func Test6390(t *testing.T) { test6390(t) } +func Test5986(t *testing.T) { test5986(t) } +func Test7665(t *testing.T) { test7665(t) } +func TestNaming(t *testing.T) { testNaming(t) } +func Test7560(t *testing.T) { test7560(t) } +func Test5242(t *testing.T) { test5242(t) } +func Test8092(t *testing.T) { test8092(t) } +func Test7978(t *testing.T) { test7978(t) } +func Test8694(t *testing.T) { test8694(t) } +func TestReturnAfterGrow(t *testing.T) { testReturnAfterGrow(t) } +func TestReturnAfterGrowFromGo(t *testing.T) { testReturnAfterGrowFromGo(t) } func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 2d14f766fc..4e5b3a2454 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -44,6 +44,7 @@ func (p *Package) writeDefs() { fmt.Fprintf(fm, "int main() { return 0; }\n") if *importRuntimeCgo { fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n") + fmt.Fprintf(fm, "char* cgo_topofstack(void) { return (char*)0; }\n") } else { // If we're not importing runtime/cgo, we *are* runtime/cgo, // which provides crosscall2. We just need a prototype. @@ -519,9 +520,13 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { // Use packed attribute to force no padding in this struct in case // gcc has different packing requirements. fmt.Fprintf(fgcc, "\t%s %v *a = v;\n", ctype, p.packedAttribute()) + if n.FuncType.Result != nil { + // Save the stack top for use below. + fmt.Fprintf(fgcc, "\tchar *stktop = cgo_topofstack();\n") + } fmt.Fprintf(fgcc, "\t") if t := n.FuncType.Result; t != nil { - fmt.Fprintf(fgcc, "a->r = ") + fmt.Fprintf(fgcc, "__typeof__(a->r) r = ") if c := t.C.String(); c[len(c)-1] == '*' { fmt.Fprint(fgcc, "(__typeof__(a->r)) ") } @@ -544,6 +549,13 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { fmt.Fprintf(fgcc, "a->p%d", i) } fmt.Fprintf(fgcc, ");\n") + if n.FuncType.Result != nil { + // The cgo call may have caused a stack copy (via a callback). + // Adjust the return value pointer appropriately. + fmt.Fprintf(fgcc, "\ta = (void*)((char*)a + (cgo_topofstack() - stktop));\n") + // Save the return value. + fmt.Fprintf(fgcc, "\ta->r = r;\n") + } if n.AddError { fmt.Fprintf(fgcc, "\treturn errno;\n") } @@ -1131,6 +1143,8 @@ __cgo_size_assert(__cgo_long_long, 8) __cgo_size_assert(float, 4) __cgo_size_assert(double, 8) +extern char* cgo_topofstack(void); + #include #include ` diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index 846a214d55..f1b3346e83 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -2275,3 +2275,13 @@ TEXT runtime·fastrand1(SB), NOSPLIT, $0-4 TEXT runtime·return0(SB), NOSPLIT, $0 MOVL $0, AX RET + +// Called from cgo wrappers, this function returns g->m->curg.stack.hi. +// Must obey the gcc calling convention. +TEXT cgo_topofstack(SB),NOSPLIT,$0 + get_tls(CX) + MOVL g(CX), AX + MOVL g_m(AX), AX + MOVL m_curg(AX), AX + MOVL (g_stack+stack_hi)(AX), AX + RET diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 7304d79a2f..b4c6c6bdca 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -2220,3 +2220,14 @@ TEXT runtime·fastrand1(SB), NOSPLIT, $0-4 TEXT runtime·return0(SB), NOSPLIT, $0 MOVL $0, AX RET + + +// Called from cgo wrappers, this function returns g->m->curg.stack.hi. +// Must obey the gcc calling convention. +TEXT cgo_topofstack(SB),NOSPLIT,$0 + get_tls(CX) + MOVQ g(CX), AX + MOVQ g_m(AX), AX + MOVQ m_curg(AX), AX + MOVQ (g_stack+stack_hi)(AX), AX + RET diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s index 38d97b78f3..2c5de8afb1 100644 --- a/src/runtime/asm_arm.s +++ b/src/runtime/asm_arm.s @@ -1300,3 +1300,11 @@ yieldloop: RET SUB $1, R1 B yieldloop + +// Called from cgo wrappers, this function returns g->m->curg.stack.hi. +// Must obey the gcc calling convention. +TEXT cgo_topofstack(SB),NOSPLIT,$0 + MOVW g_m(g), R0 + MOVW m_curg(R0), R0 + MOVW (g_stack+stack_hi)(R0), R0 + RET diff --git a/src/runtime/cgo/callbacks.c b/src/runtime/cgo/callbacks.c index 16614d03db..cea9b1667f 100644 --- a/src/runtime/cgo/callbacks.c +++ b/src/runtime/cgo/callbacks.c @@ -78,3 +78,6 @@ void (*_cgo_free)(void*) = x_cgo_free; #pragma cgo_import_static x_cgo_thread_start extern void x_cgo_thread_start(void*); void (*_cgo_thread_start)(void*) = x_cgo_thread_start; + +#pragma cgo_export_static cgo_topofstack +#pragma cgo_export_dynamic cgo_topofstack diff --git a/src/runtime/stack.c b/src/runtime/stack.c index 0d8814731c..2d23c717bd 100644 --- a/src/runtime/stack.c +++ b/src/runtime/stack.c @@ -827,7 +827,9 @@ runtime·shrinkstack(G *gp) if(used >= oldsize / 4) return; // still using at least 1/4 of the segment. - if(gp->syscallsp != 0) // TODO: can we handle this case? + // We can't copy the stack if we're in a syscall. + // The syscall might have pointers into the stack. + if(gp->syscallsp != 0) return; #ifdef GOOS_windows -- cgit v1.3 From 1aa65fe8d4c0ebdd754480d281f378fcd1c42cea Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 25 Sep 2014 08:37:04 -0700 Subject: runtime: add load_g call in arm callback. Need to restore the g register. Somehow this line vaporized from CL 144130043. Also cgo_topofstack -> _cgo_topofstack, that vaporized also. TBR=rsc CC=golang-codereviews https://golang.org/cl/150940044 --- src/cmd/cgo/out.go | 8 ++++---- src/runtime/asm_386.s | 2 +- src/runtime/asm_amd64.s | 2 +- src/runtime/asm_arm.s | 3 ++- src/runtime/cgo/callbacks.c | 4 ++-- 5 files changed, 10 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 4e5b3a2454..d92bed9bf0 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -44,7 +44,7 @@ func (p *Package) writeDefs() { fmt.Fprintf(fm, "int main() { return 0; }\n") if *importRuntimeCgo { fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n") - fmt.Fprintf(fm, "char* cgo_topofstack(void) { return (char*)0; }\n") + fmt.Fprintf(fm, "char* _cgo_topofstack(void) { return (char*)0; }\n") } else { // If we're not importing runtime/cgo, we *are* runtime/cgo, // which provides crosscall2. We just need a prototype. @@ -522,7 +522,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { fmt.Fprintf(fgcc, "\t%s %v *a = v;\n", ctype, p.packedAttribute()) if n.FuncType.Result != nil { // Save the stack top for use below. - fmt.Fprintf(fgcc, "\tchar *stktop = cgo_topofstack();\n") + fmt.Fprintf(fgcc, "\tchar *stktop = _cgo_topofstack();\n") } fmt.Fprintf(fgcc, "\t") if t := n.FuncType.Result; t != nil { @@ -552,7 +552,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { if n.FuncType.Result != nil { // The cgo call may have caused a stack copy (via a callback). // Adjust the return value pointer appropriately. - fmt.Fprintf(fgcc, "\ta = (void*)((char*)a + (cgo_topofstack() - stktop));\n") + fmt.Fprintf(fgcc, "\ta = (void*)((char*)a + (_cgo_topofstack() - stktop));\n") // Save the return value. fmt.Fprintf(fgcc, "\ta->r = r;\n") } @@ -1143,7 +1143,7 @@ __cgo_size_assert(__cgo_long_long, 8) __cgo_size_assert(float, 4) __cgo_size_assert(double, 8) -extern char* cgo_topofstack(void); +extern char* _cgo_topofstack(void); #include #include diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index f1b3346e83..1495246a25 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -2278,7 +2278,7 @@ TEXT runtime·return0(SB), NOSPLIT, $0 // Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Must obey the gcc calling convention. -TEXT cgo_topofstack(SB),NOSPLIT,$0 +TEXT _cgo_topofstack(SB),NOSPLIT,$0 get_tls(CX) MOVL g(CX), AX MOVL g_m(AX), AX diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index b4c6c6bdca..3f7f608410 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -2224,7 +2224,7 @@ TEXT runtime·return0(SB), NOSPLIT, $0 // Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Must obey the gcc calling convention. -TEXT cgo_topofstack(SB),NOSPLIT,$0 +TEXT _cgo_topofstack(SB),NOSPLIT,$0 get_tls(CX) MOVQ g(CX), AX MOVQ g_m(AX), AX diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s index 2c5de8afb1..06bd0751db 100644 --- a/src/runtime/asm_arm.s +++ b/src/runtime/asm_arm.s @@ -1303,7 +1303,8 @@ yieldloop: // Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Must obey the gcc calling convention. -TEXT cgo_topofstack(SB),NOSPLIT,$0 +TEXT _cgo_topofstack(SB),NOSPLIT,$0 + BL runtime·load_g(SB) MOVW g_m(g), R0 MOVW m_curg(R0), R0 MOVW (g_stack+stack_hi)(R0), R0 diff --git a/src/runtime/cgo/callbacks.c b/src/runtime/cgo/callbacks.c index cea9b1667f..282beeea88 100644 --- a/src/runtime/cgo/callbacks.c +++ b/src/runtime/cgo/callbacks.c @@ -79,5 +79,5 @@ void (*_cgo_free)(void*) = x_cgo_free; extern void x_cgo_thread_start(void*); void (*_cgo_thread_start)(void*) = x_cgo_thread_start; -#pragma cgo_export_static cgo_topofstack -#pragma cgo_export_dynamic cgo_topofstack +#pragma cgo_export_static _cgo_topofstack +#pragma cgo_export_dynamic _cgo_topofstack -- cgit v1.3 From 53c66543e022b1a96a599fee0819f6b16e92bead Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 25 Sep 2014 13:08:37 -0400 Subject: cmd/gc: avoid infinite recursion on invalid recursive type Fixes #8507. LGTM=iant R=golang-codereviews, iant CC=golang-codereviews, r https://golang.org/cl/144560043 --- src/cmd/gc/align.c | 4 +++- src/cmd/gc/subr.c | 3 ++- test/fixedbugs/issue8507.go | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 test/fixedbugs/issue8507.go (limited to 'src') diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c index b809640e42..6e5d149c75 100644 --- a/src/cmd/gc/align.c +++ b/src/cmd/gc/align.c @@ -119,8 +119,10 @@ dowidth(Type *t) if(t->width == -2) { lno = lineno; lineno = t->lineno; - if(!t->broke) + if(!t->broke) { + t->broke = 1; yyerror("invalid recursive type %T", t); + } t->width = 0; lineno = lno; return; diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 666be96679..c3bc5af3b8 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -529,7 +529,8 @@ algtype1(Type *t, Type **bad) if(bad) *bad = T; - + if(t->broke) + return AMEM; if(t->noalg) return ANOEQ; diff --git a/test/fixedbugs/issue8507.go b/test/fixedbugs/issue8507.go new file mode 100644 index 0000000000..00a14aa88f --- /dev/null +++ b/test/fixedbugs/issue8507.go @@ -0,0 +1,16 @@ +// errorcheck + +// Copyright 2014 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 8507 +// used to call algtype on invalid recursive type and get into infinite recursion + +package p + +type T struct{ T } // ERROR "invalid recursive type T" + +func f() { + println(T{} == T{}) +} -- cgit v1.3 From 52e9bcafe1b5bdae3354ddf82879751dfe6eb25b Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 25 Sep 2014 13:13:02 -0400 Subject: cmd/gc: print x++ (not x += 1) in errors about x++ Fixes #8311. LGTM=iant R=golang-codereviews, iant CC=golang-codereviews, r https://golang.org/cl/146270043 --- src/cmd/gc/fmt.c | 7 + src/cmd/gc/go.y | 2 + src/cmd/gc/typecheck.c | 4 + src/cmd/gc/y.tab.c | 526 ++++++++++++++++++++++---------------------- test/fixedbugs/issue8311.go | 16 ++ 5 files changed, 293 insertions(+), 262 deletions(-) create mode 100644 test/fixedbugs/issue8311.go (limited to 'src') diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c index f67757449b..89d2a14046 100644 --- a/src/cmd/gc/fmt.c +++ b/src/cmd/gc/fmt.c @@ -810,6 +810,13 @@ stmtfmt(Fmt *f, Node *n) break; case OASOP: + if(n->implicit) { + if(n->etype == OADD) + fmtprint(f, "%N++", n->left); + else + fmtprint(f, "%N--", n->left); + break; + } fmtprint(f, "%N %#O= %N", n->left, n->etype, n->right); break; diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index b16f64b5d6..68fccc1ad1 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -460,11 +460,13 @@ simple_stmt: | expr LINC { $$ = nod(OASOP, $1, nodintconst(1)); + $$->implicit = 1; $$->etype = OADD; } | expr LDEC { $$ = nod(OASOP, $1, nodintconst(1)); + $$->implicit = 1; $$->etype = OSUB; } diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 298920bfec..9440526060 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -600,6 +600,10 @@ reswitch: } if(t->etype != TIDEAL && !eqtype(l->type, r->type)) { defaultlit2(&l, &r, 1); + if(n->op == OASOP && n->implicit) { + yyerror("invalid operation: %N (non-numeric type %T)", n, l->type); + goto error; + } yyerror("invalid operation: %N (mismatched types %T and %T)", n, l->type, r->type); goto error; } diff --git a/src/cmd/gc/y.tab.c b/src/cmd/gc/y.tab.c index 47beae03b6..f464126ac9 100644 --- a/src/cmd/gc/y.tab.c +++ b/src/cmd/gc/y.tab.c @@ -663,37 +663,37 @@ static const yytype_uint16 yyrline[] = 263, 264, 271, 271, 284, 288, 289, 293, 298, 304, 308, 312, 316, 322, 328, 334, 339, 343, 347, 353, 359, 363, 367, 373, 377, 383, 384, 388, 394, 403, - 409, 427, 432, 444, 460, 465, 472, 492, 510, 519, - 538, 537, 552, 551, 583, 586, 593, 592, 603, 609, - 616, 623, 634, 640, 643, 651, 650, 661, 667, 679, - 683, 688, 678, 709, 708, 721, 724, 730, 733, 745, - 749, 744, 767, 766, 782, 783, 787, 791, 795, 799, - 803, 807, 811, 815, 819, 823, 827, 831, 835, 839, - 843, 847, 851, 855, 860, 866, 867, 871, 882, 886, - 890, 894, 899, 903, 913, 917, 922, 930, 934, 935, - 946, 950, 954, 958, 962, 970, 971, 977, 984, 990, - 997, 1000, 1007, 1013, 1030, 1037, 1038, 1045, 1046, 1065, - 1066, 1069, 1072, 1076, 1087, 1096, 1102, 1105, 1108, 1115, - 1116, 1122, 1135, 1150, 1158, 1170, 1175, 1181, 1182, 1183, - 1184, 1185, 1186, 1192, 1193, 1194, 1195, 1201, 1202, 1203, - 1204, 1205, 1211, 1212, 1215, 1218, 1219, 1220, 1221, 1222, - 1225, 1226, 1239, 1243, 1248, 1253, 1258, 1262, 1263, 1266, - 1272, 1279, 1285, 1292, 1298, 1309, 1324, 1353, 1391, 1416, - 1434, 1443, 1446, 1454, 1458, 1462, 1469, 1475, 1480, 1492, - 1495, 1506, 1507, 1513, 1514, 1520, 1524, 1530, 1531, 1537, - 1541, 1547, 1570, 1575, 1581, 1587, 1594, 1603, 1612, 1627, - 1633, 1638, 1642, 1649, 1662, 1663, 1669, 1675, 1678, 1682, - 1688, 1691, 1700, 1703, 1704, 1708, 1709, 1715, 1716, 1717, - 1718, 1719, 1721, 1720, 1735, 1741, 1745, 1749, 1753, 1757, - 1762, 1781, 1787, 1795, 1799, 1805, 1809, 1815, 1819, 1825, - 1829, 1838, 1842, 1846, 1850, 1856, 1859, 1867, 1868, 1870, - 1871, 1874, 1877, 1880, 1883, 1886, 1889, 1892, 1895, 1898, - 1901, 1904, 1907, 1910, 1913, 1919, 1923, 1927, 1931, 1935, - 1939, 1959, 1966, 1977, 1978, 1979, 1982, 1983, 1986, 1990, - 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2030, 2036, 2044, - 2052, 2058, 2065, 2081, 2103, 2107, 2113, 2116, 2119, 2123, - 2133, 2137, 2156, 2164, 2165, 2177, 2178, 2181, 2185, 2191, - 2195, 2201, 2205 + 409, 427, 432, 444, 460, 466, 474, 494, 512, 521, + 540, 539, 554, 553, 585, 588, 595, 594, 605, 611, + 618, 625, 636, 642, 645, 653, 652, 663, 669, 681, + 685, 690, 680, 711, 710, 723, 726, 732, 735, 747, + 751, 746, 769, 768, 784, 785, 789, 793, 797, 801, + 805, 809, 813, 817, 821, 825, 829, 833, 837, 841, + 845, 849, 853, 857, 862, 868, 869, 873, 884, 888, + 892, 896, 901, 905, 915, 919, 924, 932, 936, 937, + 948, 952, 956, 960, 964, 972, 973, 979, 986, 992, + 999, 1002, 1009, 1015, 1032, 1039, 1040, 1047, 1048, 1067, + 1068, 1071, 1074, 1078, 1089, 1098, 1104, 1107, 1110, 1117, + 1118, 1124, 1137, 1152, 1160, 1172, 1177, 1183, 1184, 1185, + 1186, 1187, 1188, 1194, 1195, 1196, 1197, 1203, 1204, 1205, + 1206, 1207, 1213, 1214, 1217, 1220, 1221, 1222, 1223, 1224, + 1227, 1228, 1241, 1245, 1250, 1255, 1260, 1264, 1265, 1268, + 1274, 1281, 1287, 1294, 1300, 1311, 1326, 1355, 1393, 1418, + 1436, 1445, 1448, 1456, 1460, 1464, 1471, 1477, 1482, 1494, + 1497, 1508, 1509, 1515, 1516, 1522, 1526, 1532, 1533, 1539, + 1543, 1549, 1572, 1577, 1583, 1589, 1596, 1605, 1614, 1629, + 1635, 1640, 1644, 1651, 1664, 1665, 1671, 1677, 1680, 1684, + 1690, 1693, 1702, 1705, 1706, 1710, 1711, 1717, 1718, 1719, + 1720, 1721, 1723, 1722, 1737, 1743, 1747, 1751, 1755, 1759, + 1764, 1783, 1789, 1797, 1801, 1807, 1811, 1817, 1821, 1827, + 1831, 1840, 1844, 1848, 1852, 1858, 1861, 1869, 1870, 1872, + 1873, 1876, 1879, 1882, 1885, 1888, 1891, 1894, 1897, 1900, + 1903, 1906, 1909, 1912, 1915, 1921, 1925, 1929, 1933, 1937, + 1941, 1961, 1968, 1979, 1980, 1981, 1984, 1985, 1988, 1992, + 2002, 2006, 2010, 2014, 2018, 2022, 2026, 2032, 2038, 2046, + 2054, 2060, 2067, 2083, 2105, 2109, 2115, 2118, 2121, 2125, + 2135, 2139, 2158, 2166, 2167, 2179, 2180, 2183, 2187, 2193, + 2197, 2203, 2207 }; #endif @@ -2781,20 +2781,22 @@ yyreduce: #line 461 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1)); + (yyval.node)->implicit = 1; (yyval.node)->etype = OADD; } break; case 55: -#line 466 "go.y" +#line 467 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1)); + (yyval.node)->implicit = 1; (yyval.node)->etype = OSUB; } break; case 56: -#line 473 "go.y" +#line 475 "go.y" { Node *n, *nn; @@ -2817,7 +2819,7 @@ yyreduce: break; case 57: -#line 493 "go.y" +#line 495 "go.y" { Node *n; @@ -2838,7 +2840,7 @@ yyreduce: break; case 58: -#line 511 "go.y" +#line 513 "go.y" { // will be converted to OCASE // right will point to next case @@ -2850,7 +2852,7 @@ yyreduce: break; case 59: -#line 520 "go.y" +#line 522 "go.y" { Node *n, *nn; @@ -2869,14 +2871,14 @@ yyreduce: break; case 60: -#line 538 "go.y" +#line 540 "go.y" { markdcl(); } break; case 61: -#line 542 "go.y" +#line 544 "go.y" { if((yyvsp[(3) - (4)].list) == nil) (yyval.node) = nod(OEMPTY, N, N); @@ -2887,7 +2889,7 @@ yyreduce: break; case 62: -#line 552 "go.y" +#line 554 "go.y" { // If the last token read by the lexer was consumed // as part of the case, clear it (parser has cleared yychar). @@ -2901,7 +2903,7 @@ yyreduce: break; case 63: -#line 563 "go.y" +#line 565 "go.y" { int last; @@ -2923,28 +2925,28 @@ yyreduce: break; case 64: -#line 583 "go.y" +#line 585 "go.y" { (yyval.list) = nil; } break; case 65: -#line 587 "go.y" +#line 589 "go.y" { (yyval.list) = list((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node)); } break; case 66: -#line 593 "go.y" +#line 595 "go.y" { markdcl(); } break; case 67: -#line 597 "go.y" +#line 599 "go.y" { (yyval.list) = (yyvsp[(3) - (4)].list); popdcl(); @@ -2952,7 +2954,7 @@ yyreduce: break; case 68: -#line 604 "go.y" +#line 606 "go.y" { (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node)); (yyval.node)->list = (yyvsp[(1) - (4)].list); @@ -2961,7 +2963,7 @@ yyreduce: break; case 69: -#line 610 "go.y" +#line 612 "go.y" { (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node)); (yyval.node)->list = (yyvsp[(1) - (4)].list); @@ -2971,7 +2973,7 @@ yyreduce: break; case 70: -#line 617 "go.y" +#line 619 "go.y" { (yyval.node) = nod(ORANGE, N, (yyvsp[(2) - (2)].node)); (yyval.node)->etype = 0; // := flag @@ -2979,7 +2981,7 @@ yyreduce: break; case 71: -#line 624 "go.y" +#line 626 "go.y" { // init ; test ; incr if((yyvsp[(5) - (5)].node) != N && (yyvsp[(5) - (5)].node)->colas != 0) @@ -2993,7 +2995,7 @@ yyreduce: break; case 72: -#line 635 "go.y" +#line 637 "go.y" { // normal test (yyval.node) = nod(OFOR, N, N); @@ -3002,7 +3004,7 @@ yyreduce: break; case 74: -#line 644 "go.y" +#line 646 "go.y" { (yyval.node) = (yyvsp[(1) - (2)].node); (yyval.node)->nbody = concat((yyval.node)->nbody, (yyvsp[(2) - (2)].list)); @@ -3010,14 +3012,14 @@ yyreduce: break; case 75: -#line 651 "go.y" +#line 653 "go.y" { markdcl(); } break; case 76: -#line 655 "go.y" +#line 657 "go.y" { (yyval.node) = (yyvsp[(3) - (3)].node); popdcl(); @@ -3025,7 +3027,7 @@ yyreduce: break; case 77: -#line 662 "go.y" +#line 664 "go.y" { // test (yyval.node) = nod(OIF, N, N); @@ -3034,7 +3036,7 @@ yyreduce: break; case 78: -#line 668 "go.y" +#line 670 "go.y" { // init ; test (yyval.node) = nod(OIF, N, N); @@ -3045,14 +3047,14 @@ yyreduce: break; case 79: -#line 679 "go.y" +#line 681 "go.y" { markdcl(); } break; case 80: -#line 683 "go.y" +#line 685 "go.y" { if((yyvsp[(3) - (3)].node)->ntest == N) yyerror("missing condition in if statement"); @@ -3060,14 +3062,14 @@ yyreduce: break; case 81: -#line 688 "go.y" +#line 690 "go.y" { (yyvsp[(3) - (5)].node)->nbody = (yyvsp[(5) - (5)].list); } break; case 82: -#line 692 "go.y" +#line 694 "go.y" { Node *n; NodeList *nn; @@ -3085,14 +3087,14 @@ yyreduce: break; case 83: -#line 709 "go.y" +#line 711 "go.y" { markdcl(); } break; case 84: -#line 713 "go.y" +#line 715 "go.y" { if((yyvsp[(4) - (5)].node)->ntest == N) yyerror("missing condition in if statement"); @@ -3102,28 +3104,28 @@ yyreduce: break; case 85: -#line 721 "go.y" +#line 723 "go.y" { (yyval.list) = nil; } break; case 86: -#line 725 "go.y" +#line 727 "go.y" { (yyval.list) = concat((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].list)); } break; case 87: -#line 730 "go.y" +#line 732 "go.y" { (yyval.list) = nil; } break; case 88: -#line 734 "go.y" +#line 736 "go.y" { NodeList *node; @@ -3135,14 +3137,14 @@ yyreduce: break; case 89: -#line 745 "go.y" +#line 747 "go.y" { markdcl(); } break; case 90: -#line 749 "go.y" +#line 751 "go.y" { Node *n; n = (yyvsp[(3) - (3)].node)->ntest; @@ -3153,7 +3155,7 @@ yyreduce: break; case 91: -#line 757 "go.y" +#line 759 "go.y" { (yyval.node) = (yyvsp[(3) - (7)].node); (yyval.node)->op = OSWITCH; @@ -3164,14 +3166,14 @@ yyreduce: break; case 92: -#line 767 "go.y" +#line 769 "go.y" { typesw = nod(OXXX, typesw, N); } break; case 93: -#line 771 "go.y" +#line 773 "go.y" { (yyval.node) = nod(OSELECT, N, N); (yyval.node)->lineno = typesw->lineno; @@ -3181,154 +3183,154 @@ yyreduce: break; case 95: -#line 784 "go.y" +#line 786 "go.y" { (yyval.node) = nod(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 96: -#line 788 "go.y" +#line 790 "go.y" { (yyval.node) = nod(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 97: -#line 792 "go.y" +#line 794 "go.y" { (yyval.node) = nod(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 98: -#line 796 "go.y" +#line 798 "go.y" { (yyval.node) = nod(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 99: -#line 800 "go.y" +#line 802 "go.y" { (yyval.node) = nod(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 100: -#line 804 "go.y" +#line 806 "go.y" { (yyval.node) = nod(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 101: -#line 808 "go.y" +#line 810 "go.y" { (yyval.node) = nod(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 102: -#line 812 "go.y" +#line 814 "go.y" { (yyval.node) = nod(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 103: -#line 816 "go.y" +#line 818 "go.y" { (yyval.node) = nod(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 104: -#line 820 "go.y" +#line 822 "go.y" { (yyval.node) = nod(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 105: -#line 824 "go.y" +#line 826 "go.y" { (yyval.node) = nod(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 106: -#line 828 "go.y" +#line 830 "go.y" { (yyval.node) = nod(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 107: -#line 832 "go.y" +#line 834 "go.y" { (yyval.node) = nod(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 108: -#line 836 "go.y" +#line 838 "go.y" { (yyval.node) = nod(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 109: -#line 840 "go.y" +#line 842 "go.y" { (yyval.node) = nod(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 110: -#line 844 "go.y" +#line 846 "go.y" { (yyval.node) = nod(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 111: -#line 848 "go.y" +#line 850 "go.y" { (yyval.node) = nod(OANDNOT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 112: -#line 852 "go.y" +#line 854 "go.y" { (yyval.node) = nod(OLSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 113: -#line 856 "go.y" +#line 858 "go.y" { (yyval.node) = nod(ORSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 114: -#line 861 "go.y" +#line 863 "go.y" { (yyval.node) = nod(OSEND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 116: -#line 868 "go.y" +#line 870 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; case 117: -#line 872 "go.y" +#line 874 "go.y" { if((yyvsp[(2) - (2)].node)->op == OCOMPLIT) { // Special case for &T{...}: turn into (*T){...}. @@ -3342,28 +3344,28 @@ yyreduce: break; case 118: -#line 883 "go.y" +#line 885 "go.y" { (yyval.node) = nod(OPLUS, (yyvsp[(2) - (2)].node), N); } break; case 119: -#line 887 "go.y" +#line 889 "go.y" { (yyval.node) = nod(OMINUS, (yyvsp[(2) - (2)].node), N); } break; case 120: -#line 891 "go.y" +#line 893 "go.y" { (yyval.node) = nod(ONOT, (yyvsp[(2) - (2)].node), N); } break; case 121: -#line 895 "go.y" +#line 897 "go.y" { yyerror("the bitwise complement operator is ^"); (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N); @@ -3371,28 +3373,28 @@ yyreduce: break; case 122: -#line 900 "go.y" +#line 902 "go.y" { (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N); } break; case 123: -#line 904 "go.y" +#line 906 "go.y" { (yyval.node) = nod(ORECV, (yyvsp[(2) - (2)].node), N); } break; case 124: -#line 914 "go.y" +#line 916 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (3)].node), N); } break; case 125: -#line 918 "go.y" +#line 920 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -3400,7 +3402,7 @@ yyreduce: break; case 126: -#line 923 "go.y" +#line 925 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (6)].node), N); (yyval.node)->list = (yyvsp[(3) - (6)].list); @@ -3409,14 +3411,14 @@ yyreduce: break; case 127: -#line 931 "go.y" +#line 933 "go.y" { (yyval.node) = nodlit((yyvsp[(1) - (1)].val)); } break; case 129: -#line 936 "go.y" +#line 938 "go.y" { if((yyvsp[(1) - (3)].node)->op == OPACK) { Sym *s; @@ -3430,35 +3432,35 @@ yyreduce: break; case 130: -#line 947 "go.y" +#line 949 "go.y" { (yyval.node) = nod(ODOTTYPE, (yyvsp[(1) - (5)].node), (yyvsp[(4) - (5)].node)); } break; case 131: -#line 951 "go.y" +#line 953 "go.y" { (yyval.node) = nod(OTYPESW, N, (yyvsp[(1) - (5)].node)); } break; case 132: -#line 955 "go.y" +#line 957 "go.y" { (yyval.node) = nod(OINDEX, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)); } break; case 133: -#line 959 "go.y" +#line 961 "go.y" { (yyval.node) = nod(OSLICE, (yyvsp[(1) - (6)].node), nod(OKEY, (yyvsp[(3) - (6)].node), (yyvsp[(5) - (6)].node))); } break; case 134: -#line 963 "go.y" +#line 965 "go.y" { if((yyvsp[(5) - (8)].node) == N) yyerror("middle index required in 3-index slice"); @@ -3469,7 +3471,7 @@ yyreduce: break; case 136: -#line 972 "go.y" +#line 974 "go.y" { // conversion (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N); @@ -3478,7 +3480,7 @@ yyreduce: break; case 137: -#line 978 "go.y" +#line 980 "go.y" { (yyval.node) = (yyvsp[(3) - (5)].node); (yyval.node)->right = (yyvsp[(1) - (5)].node); @@ -3488,7 +3490,7 @@ yyreduce: break; case 138: -#line 985 "go.y" +#line 987 "go.y" { (yyval.node) = (yyvsp[(3) - (5)].node); (yyval.node)->right = (yyvsp[(1) - (5)].node); @@ -3497,7 +3499,7 @@ yyreduce: break; case 139: -#line 991 "go.y" +#line 993 "go.y" { yyerror("cannot parenthesize type in composite literal"); (yyval.node) = (yyvsp[(5) - (7)].node); @@ -3507,7 +3509,7 @@ yyreduce: break; case 141: -#line 1000 "go.y" +#line 1002 "go.y" { // composite expression. // make node early so we get the right line number. @@ -3516,14 +3518,14 @@ yyreduce: break; case 142: -#line 1008 "go.y" +#line 1010 "go.y" { (yyval.node) = nod(OKEY, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 143: -#line 1014 "go.y" +#line 1016 "go.y" { // These nodes do not carry line numbers. // Since a composite literal commonly spans several lines, @@ -3543,7 +3545,7 @@ yyreduce: break; case 144: -#line 1031 "go.y" +#line 1033 "go.y" { (yyval.node) = (yyvsp[(2) - (4)].node); (yyval.node)->list = (yyvsp[(3) - (4)].list); @@ -3551,7 +3553,7 @@ yyreduce: break; case 146: -#line 1039 "go.y" +#line 1041 "go.y" { (yyval.node) = (yyvsp[(2) - (4)].node); (yyval.node)->list = (yyvsp[(3) - (4)].list); @@ -3559,7 +3561,7 @@ yyreduce: break; case 148: -#line 1047 "go.y" +#line 1049 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); @@ -3579,21 +3581,21 @@ yyreduce: break; case 152: -#line 1073 "go.y" +#line 1075 "go.y" { (yyval.i) = LBODY; } break; case 153: -#line 1077 "go.y" +#line 1079 "go.y" { (yyval.i) = '{'; } break; case 154: -#line 1088 "go.y" +#line 1090 "go.y" { if((yyvsp[(1) - (1)].sym) == S) (yyval.node) = N; @@ -3603,21 +3605,21 @@ yyreduce: break; case 155: -#line 1097 "go.y" +#line 1099 "go.y" { (yyval.node) = dclname((yyvsp[(1) - (1)].sym)); } break; case 156: -#line 1102 "go.y" +#line 1104 "go.y" { (yyval.node) = N; } break; case 158: -#line 1109 "go.y" +#line 1111 "go.y" { (yyval.sym) = (yyvsp[(1) - (1)].sym); // during imports, unqualified non-exported identifiers are from builtinpkg @@ -3627,14 +3629,14 @@ yyreduce: break; case 160: -#line 1117 "go.y" +#line 1119 "go.y" { (yyval.sym) = S; } break; case 161: -#line 1123 "go.y" +#line 1125 "go.y" { Pkg *p; @@ -3650,7 +3652,7 @@ yyreduce: break; case 162: -#line 1136 "go.y" +#line 1138 "go.y" { Pkg *p; @@ -3666,7 +3668,7 @@ yyreduce: break; case 163: -#line 1151 "go.y" +#line 1153 "go.y" { (yyval.node) = oldname((yyvsp[(1) - (1)].sym)); if((yyval.node)->pack != N) @@ -3675,7 +3677,7 @@ yyreduce: break; case 165: -#line 1171 "go.y" +#line 1173 "go.y" { yyerror("final argument in variadic function missing type"); (yyval.node) = nod(ODDD, typenod(typ(TINTER)), N); @@ -3683,35 +3685,35 @@ yyreduce: break; case 166: -#line 1176 "go.y" +#line 1178 "go.y" { (yyval.node) = nod(ODDD, (yyvsp[(2) - (2)].node), N); } break; case 172: -#line 1187 "go.y" +#line 1189 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); } break; case 176: -#line 1196 "go.y" +#line 1198 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; case 181: -#line 1206 "go.y" +#line 1208 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); } break; case 191: -#line 1227 "go.y" +#line 1229 "go.y" { if((yyvsp[(1) - (3)].node)->op == OPACK) { Sym *s; @@ -3725,14 +3727,14 @@ yyreduce: break; case 192: -#line 1240 "go.y" +#line 1242 "go.y" { (yyval.node) = nod(OTARRAY, (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].node)); } break; case 193: -#line 1244 "go.y" +#line 1246 "go.y" { // array literal of nelem (yyval.node) = nod(OTARRAY, nod(ODDD, N, N), (yyvsp[(4) - (4)].node)); @@ -3740,7 +3742,7 @@ yyreduce: break; case 194: -#line 1249 "go.y" +#line 1251 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(2) - (2)].node), N); (yyval.node)->etype = Cboth; @@ -3748,7 +3750,7 @@ yyreduce: break; case 195: -#line 1254 "go.y" +#line 1256 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N); (yyval.node)->etype = Csend; @@ -3756,21 +3758,21 @@ yyreduce: break; case 196: -#line 1259 "go.y" +#line 1261 "go.y" { (yyval.node) = nod(OTMAP, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node)); } break; case 199: -#line 1267 "go.y" +#line 1269 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; case 200: -#line 1273 "go.y" +#line 1275 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N); (yyval.node)->etype = Crecv; @@ -3778,7 +3780,7 @@ yyreduce: break; case 201: -#line 1280 "go.y" +#line 1282 "go.y" { (yyval.node) = nod(OTSTRUCT, N, N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -3787,7 +3789,7 @@ yyreduce: break; case 202: -#line 1286 "go.y" +#line 1288 "go.y" { (yyval.node) = nod(OTSTRUCT, N, N); fixlbrace((yyvsp[(2) - (3)].i)); @@ -3795,7 +3797,7 @@ yyreduce: break; case 203: -#line 1293 "go.y" +#line 1295 "go.y" { (yyval.node) = nod(OTINTER, N, N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -3804,7 +3806,7 @@ yyreduce: break; case 204: -#line 1299 "go.y" +#line 1301 "go.y" { (yyval.node) = nod(OTINTER, N, N); fixlbrace((yyvsp[(2) - (3)].i)); @@ -3812,7 +3814,7 @@ yyreduce: break; case 205: -#line 1310 "go.y" +#line 1312 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); if((yyval.node) == N) @@ -3828,7 +3830,7 @@ yyreduce: break; case 206: -#line 1325 "go.y" +#line 1327 "go.y" { Node *t; @@ -3860,7 +3862,7 @@ yyreduce: break; case 207: -#line 1354 "go.y" +#line 1356 "go.y" { Node *rcvr, *t; @@ -3899,7 +3901,7 @@ yyreduce: break; case 208: -#line 1392 "go.y" +#line 1394 "go.y" { Sym *s; Type *t; @@ -3927,7 +3929,7 @@ yyreduce: break; case 209: -#line 1417 "go.y" +#line 1419 "go.y" { (yyval.node) = methodname1(newname((yyvsp[(4) - (8)].sym)), (yyvsp[(2) - (8)].list)->n->right); (yyval.node)->type = functype((yyvsp[(2) - (8)].list)->n, (yyvsp[(6) - (8)].list), (yyvsp[(8) - (8)].list)); @@ -3946,7 +3948,7 @@ yyreduce: break; case 210: -#line 1435 "go.y" +#line 1437 "go.y" { (yyvsp[(3) - (5)].list) = checkarglist((yyvsp[(3) - (5)].list), 1); (yyval.node) = nod(OTFUNC, N, N); @@ -3956,14 +3958,14 @@ yyreduce: break; case 211: -#line 1443 "go.y" +#line 1445 "go.y" { (yyval.list) = nil; } break; case 212: -#line 1447 "go.y" +#line 1449 "go.y" { (yyval.list) = (yyvsp[(2) - (3)].list); if((yyval.list) == nil) @@ -3972,21 +3974,21 @@ yyreduce: break; case 213: -#line 1455 "go.y" +#line 1457 "go.y" { (yyval.list) = nil; } break; case 214: -#line 1459 "go.y" +#line 1461 "go.y" { (yyval.list) = list1(nod(ODCLFIELD, N, (yyvsp[(1) - (1)].node))); } break; case 215: -#line 1463 "go.y" +#line 1465 "go.y" { (yyvsp[(2) - (3)].list) = checkarglist((yyvsp[(2) - (3)].list), 0); (yyval.list) = (yyvsp[(2) - (3)].list); @@ -3994,14 +3996,14 @@ yyreduce: break; case 216: -#line 1470 "go.y" +#line 1472 "go.y" { closurehdr((yyvsp[(1) - (1)].node)); } break; case 217: -#line 1476 "go.y" +#line 1478 "go.y" { (yyval.node) = closurebody((yyvsp[(3) - (4)].list)); fixlbrace((yyvsp[(2) - (4)].i)); @@ -4009,21 +4011,21 @@ yyreduce: break; case 218: -#line 1481 "go.y" +#line 1483 "go.y" { (yyval.node) = closurebody(nil); } break; case 219: -#line 1492 "go.y" +#line 1494 "go.y" { (yyval.list) = nil; } break; case 220: -#line 1496 "go.y" +#line 1498 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(2) - (3)].list)); if(nsyntaxerrors == 0) @@ -4035,56 +4037,56 @@ yyreduce: break; case 222: -#line 1508 "go.y" +#line 1510 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; case 224: -#line 1515 "go.y" +#line 1517 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; case 225: -#line 1521 "go.y" +#line 1523 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 226: -#line 1525 "go.y" +#line 1527 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 228: -#line 1532 "go.y" +#line 1534 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; case 229: -#line 1538 "go.y" +#line 1540 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 230: -#line 1542 "go.y" +#line 1544 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 231: -#line 1548 "go.y" +#line 1550 "go.y" { NodeList *l; @@ -4110,7 +4112,7 @@ yyreduce: break; case 232: -#line 1571 "go.y" +#line 1573 "go.y" { (yyvsp[(1) - (2)].node)->val = (yyvsp[(2) - (2)].val); (yyval.list) = list1((yyvsp[(1) - (2)].node)); @@ -4118,7 +4120,7 @@ yyreduce: break; case 233: -#line 1576 "go.y" +#line 1578 "go.y" { (yyvsp[(2) - (4)].node)->val = (yyvsp[(4) - (4)].val); (yyval.list) = list1((yyvsp[(2) - (4)].node)); @@ -4127,7 +4129,7 @@ yyreduce: break; case 234: -#line 1582 "go.y" +#line 1584 "go.y" { (yyvsp[(2) - (3)].node)->right = nod(OIND, (yyvsp[(2) - (3)].node)->right, N); (yyvsp[(2) - (3)].node)->val = (yyvsp[(3) - (3)].val); @@ -4136,7 +4138,7 @@ yyreduce: break; case 235: -#line 1588 "go.y" +#line 1590 "go.y" { (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N); (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val); @@ -4146,7 +4148,7 @@ yyreduce: break; case 236: -#line 1595 "go.y" +#line 1597 "go.y" { (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N); (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val); @@ -4156,7 +4158,7 @@ yyreduce: break; case 237: -#line 1604 "go.y" +#line 1606 "go.y" { Node *n; @@ -4168,7 +4170,7 @@ yyreduce: break; case 238: -#line 1613 "go.y" +#line 1615 "go.y" { Pkg *pkg; @@ -4184,14 +4186,14 @@ yyreduce: break; case 239: -#line 1628 "go.y" +#line 1630 "go.y" { (yyval.node) = embedded((yyvsp[(1) - (1)].sym), localpkg); } break; case 240: -#line 1634 "go.y" +#line 1636 "go.y" { (yyval.node) = nod(ODCLFIELD, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); ifacedcl((yyval.node)); @@ -4199,14 +4201,14 @@ yyreduce: break; case 241: -#line 1639 "go.y" +#line 1641 "go.y" { (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(1) - (1)].sym))); } break; case 242: -#line 1643 "go.y" +#line 1645 "go.y" { (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(2) - (3)].sym))); yyerror("cannot parenthesize embedded type"); @@ -4214,7 +4216,7 @@ yyreduce: break; case 243: -#line 1650 "go.y" +#line 1652 "go.y" { // without func keyword (yyvsp[(2) - (4)].list) = checkarglist((yyvsp[(2) - (4)].list), 1); @@ -4225,7 +4227,7 @@ yyreduce: break; case 245: -#line 1664 "go.y" +#line 1666 "go.y" { (yyval.node) = nod(ONONAME, N, N); (yyval.node)->sym = (yyvsp[(1) - (2)].sym); @@ -4234,7 +4236,7 @@ yyreduce: break; case 246: -#line 1670 "go.y" +#line 1672 "go.y" { (yyval.node) = nod(ONONAME, N, N); (yyval.node)->sym = (yyvsp[(1) - (2)].sym); @@ -4243,56 +4245,56 @@ yyreduce: break; case 248: -#line 1679 "go.y" +#line 1681 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 249: -#line 1683 "go.y" +#line 1685 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 250: -#line 1688 "go.y" +#line 1690 "go.y" { (yyval.list) = nil; } break; case 251: -#line 1692 "go.y" +#line 1694 "go.y" { (yyval.list) = (yyvsp[(1) - (2)].list); } break; case 252: -#line 1700 "go.y" +#line 1702 "go.y" { (yyval.node) = N; } break; case 254: -#line 1705 "go.y" +#line 1707 "go.y" { (yyval.node) = liststmt((yyvsp[(1) - (1)].list)); } break; case 256: -#line 1710 "go.y" +#line 1712 "go.y" { (yyval.node) = N; } break; case 262: -#line 1721 "go.y" +#line 1723 "go.y" { (yyvsp[(1) - (2)].node) = nod(OLABEL, (yyvsp[(1) - (2)].node), N); (yyvsp[(1) - (2)].node)->sym = dclstack; // context, for goto restrictions @@ -4300,7 +4302,7 @@ yyreduce: break; case 263: -#line 1726 "go.y" +#line 1728 "go.y" { NodeList *l; @@ -4313,7 +4315,7 @@ yyreduce: break; case 264: -#line 1736 "go.y" +#line 1738 "go.y" { // will be converted to OFALL (yyval.node) = nod(OXFALL, N, N); @@ -4322,35 +4324,35 @@ yyreduce: break; case 265: -#line 1742 "go.y" +#line 1744 "go.y" { (yyval.node) = nod(OBREAK, (yyvsp[(2) - (2)].node), N); } break; case 266: -#line 1746 "go.y" +#line 1748 "go.y" { (yyval.node) = nod(OCONTINUE, (yyvsp[(2) - (2)].node), N); } break; case 267: -#line 1750 "go.y" +#line 1752 "go.y" { (yyval.node) = nod(OPROC, (yyvsp[(2) - (2)].node), N); } break; case 268: -#line 1754 "go.y" +#line 1756 "go.y" { (yyval.node) = nod(ODEFER, (yyvsp[(2) - (2)].node), N); } break; case 269: -#line 1758 "go.y" +#line 1760 "go.y" { (yyval.node) = nod(OGOTO, (yyvsp[(2) - (2)].node), N); (yyval.node)->sym = dclstack; // context, for goto restrictions @@ -4358,7 +4360,7 @@ yyreduce: break; case 270: -#line 1763 "go.y" +#line 1765 "go.y" { (yyval.node) = nod(ORETURN, N, N); (yyval.node)->list = (yyvsp[(2) - (2)].list); @@ -4378,7 +4380,7 @@ yyreduce: break; case 271: -#line 1782 "go.y" +#line 1784 "go.y" { (yyval.list) = nil; if((yyvsp[(1) - (1)].node) != N) @@ -4387,7 +4389,7 @@ yyreduce: break; case 272: -#line 1788 "go.y" +#line 1790 "go.y" { (yyval.list) = (yyvsp[(1) - (3)].list); if((yyvsp[(3) - (3)].node) != N) @@ -4396,189 +4398,189 @@ yyreduce: break; case 273: -#line 1796 "go.y" +#line 1798 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 274: -#line 1800 "go.y" +#line 1802 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 275: -#line 1806 "go.y" +#line 1808 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 276: -#line 1810 "go.y" +#line 1812 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 277: -#line 1816 "go.y" +#line 1818 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 278: -#line 1820 "go.y" +#line 1822 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 279: -#line 1826 "go.y" +#line 1828 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 280: -#line 1830 "go.y" +#line 1832 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 281: -#line 1839 "go.y" +#line 1841 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 282: -#line 1843 "go.y" +#line 1845 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 283: -#line 1847 "go.y" +#line 1849 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 284: -#line 1851 "go.y" +#line 1853 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 285: -#line 1856 "go.y" +#line 1858 "go.y" { (yyval.list) = nil; } break; case 286: -#line 1860 "go.y" +#line 1862 "go.y" { (yyval.list) = (yyvsp[(1) - (2)].list); } break; case 291: -#line 1874 "go.y" +#line 1876 "go.y" { (yyval.node) = N; } break; case 293: -#line 1880 "go.y" +#line 1882 "go.y" { (yyval.list) = nil; } break; case 295: -#line 1886 "go.y" +#line 1888 "go.y" { (yyval.node) = N; } break; case 297: -#line 1892 "go.y" +#line 1894 "go.y" { (yyval.list) = nil; } break; case 299: -#line 1898 "go.y" +#line 1900 "go.y" { (yyval.list) = nil; } break; case 301: -#line 1904 "go.y" +#line 1906 "go.y" { (yyval.list) = nil; } break; case 303: -#line 1910 "go.y" +#line 1912 "go.y" { (yyval.val).ctype = CTxxx; } break; case 305: -#line 1920 "go.y" +#line 1922 "go.y" { importimport((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].val).u.sval); } break; case 306: -#line 1924 "go.y" +#line 1926 "go.y" { importvar((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].type)); } break; case 307: -#line 1928 "go.y" +#line 1930 "go.y" { importconst((yyvsp[(2) - (5)].sym), types[TIDEAL], (yyvsp[(4) - (5)].node)); } break; case 308: -#line 1932 "go.y" +#line 1934 "go.y" { importconst((yyvsp[(2) - (6)].sym), (yyvsp[(3) - (6)].type), (yyvsp[(5) - (6)].node)); } break; case 309: -#line 1936 "go.y" +#line 1938 "go.y" { importtype((yyvsp[(2) - (4)].type), (yyvsp[(3) - (4)].type)); } break; case 310: -#line 1940 "go.y" +#line 1942 "go.y" { if((yyvsp[(2) - (4)].node) == N) { dclcontext = PEXTERN; // since we skip the funcbody below @@ -4599,7 +4601,7 @@ yyreduce: break; case 311: -#line 1960 "go.y" +#line 1962 "go.y" { (yyval.sym) = (yyvsp[(1) - (1)].sym); structpkg = (yyval.sym)->pkg; @@ -4607,7 +4609,7 @@ yyreduce: break; case 312: -#line 1967 "go.y" +#line 1969 "go.y" { (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym)); importsym((yyvsp[(1) - (1)].sym), OTYPE); @@ -4615,14 +4617,14 @@ yyreduce: break; case 318: -#line 1987 "go.y" +#line 1989 "go.y" { (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym)); } break; case 319: -#line 1991 "go.y" +#line 1993 "go.y" { // predefined name like uint8 (yyvsp[(1) - (1)].sym) = pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg); @@ -4635,49 +4637,49 @@ yyreduce: break; case 320: -#line 2001 "go.y" +#line 2003 "go.y" { (yyval.type) = aindex(N, (yyvsp[(3) - (3)].type)); } break; case 321: -#line 2005 "go.y" +#line 2007 "go.y" { (yyval.type) = aindex(nodlit((yyvsp[(2) - (4)].val)), (yyvsp[(4) - (4)].type)); } break; case 322: -#line 2009 "go.y" +#line 2011 "go.y" { (yyval.type) = maptype((yyvsp[(3) - (5)].type), (yyvsp[(5) - (5)].type)); } break; case 323: -#line 2013 "go.y" +#line 2015 "go.y" { (yyval.type) = tostruct((yyvsp[(3) - (4)].list)); } break; case 324: -#line 2017 "go.y" +#line 2019 "go.y" { (yyval.type) = tointerface((yyvsp[(3) - (4)].list)); } break; case 325: -#line 2021 "go.y" +#line 2023 "go.y" { (yyval.type) = ptrto((yyvsp[(2) - (2)].type)); } break; case 326: -#line 2025 "go.y" +#line 2027 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(2) - (2)].type); @@ -4686,7 +4688,7 @@ yyreduce: break; case 327: -#line 2031 "go.y" +#line 2033 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (4)].type); @@ -4695,7 +4697,7 @@ yyreduce: break; case 328: -#line 2037 "go.y" +#line 2039 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (3)].type); @@ -4704,7 +4706,7 @@ yyreduce: break; case 329: -#line 2045 "go.y" +#line 2047 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (3)].type); @@ -4713,14 +4715,14 @@ yyreduce: break; case 330: -#line 2053 "go.y" +#line 2055 "go.y" { (yyval.type) = functype(nil, (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list)); } break; case 331: -#line 2059 "go.y" +#line 2061 "go.y" { (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(2) - (3)].type))); if((yyvsp[(1) - (3)].sym)) @@ -4730,7 +4732,7 @@ yyreduce: break; case 332: -#line 2066 "go.y" +#line 2068 "go.y" { Type *t; @@ -4747,7 +4749,7 @@ yyreduce: break; case 333: -#line 2082 "go.y" +#line 2084 "go.y" { Sym *s; Pkg *p; @@ -4770,49 +4772,49 @@ yyreduce: break; case 334: -#line 2104 "go.y" +#line 2106 "go.y" { (yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (5)].sym)), typenod(functype(fakethis(), (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list)))); } break; case 335: -#line 2108 "go.y" +#line 2110 "go.y" { (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type))); } break; case 336: -#line 2113 "go.y" +#line 2115 "go.y" { (yyval.list) = nil; } break; case 338: -#line 2120 "go.y" +#line 2122 "go.y" { (yyval.list) = (yyvsp[(2) - (3)].list); } break; case 339: -#line 2124 "go.y" +#line 2126 "go.y" { (yyval.list) = list1(nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type)))); } break; case 340: -#line 2134 "go.y" +#line 2136 "go.y" { (yyval.node) = nodlit((yyvsp[(1) - (1)].val)); } break; case 341: -#line 2138 "go.y" +#line 2140 "go.y" { (yyval.node) = nodlit((yyvsp[(2) - (2)].val)); switch((yyval.node)->val.ctype){ @@ -4834,7 +4836,7 @@ yyreduce: break; case 342: -#line 2157 "go.y" +#line 2159 "go.y" { (yyval.node) = oldname(pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg)); if((yyval.node)->op != OLITERAL) @@ -4843,7 +4845,7 @@ yyreduce: break; case 344: -#line 2166 "go.y" +#line 2168 "go.y" { if((yyvsp[(2) - (5)].node)->val.ctype == CTRUNE && (yyvsp[(4) - (5)].node)->val.ctype == CTINT) { (yyval.node) = (yyvsp[(2) - (5)].node); @@ -4857,42 +4859,42 @@ yyreduce: break; case 347: -#line 2182 "go.y" +#line 2184 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 348: -#line 2186 "go.y" +#line 2188 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 349: -#line 2192 "go.y" +#line 2194 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 350: -#line 2196 "go.y" +#line 2198 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 351: -#line 2202 "go.y" +#line 2204 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 352: -#line 2206 "go.y" +#line 2208 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } @@ -4900,7 +4902,7 @@ yyreduce: /* Line 1267 of yacc.c. */ -#line 4905 "y.tab.c" +#line 4907 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); @@ -5114,7 +5116,7 @@ yyreturn: } -#line 2210 "go.y" +#line 2212 "go.y" static void diff --git a/test/fixedbugs/issue8311.go b/test/fixedbugs/issue8311.go new file mode 100644 index 0000000000..dd928566d6 --- /dev/null +++ b/test/fixedbugs/issue8311.go @@ -0,0 +1,16 @@ +// errorcheck + +// Copyright 2014 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 8311. +// error for x++ should say x++ not x += 1 + +package p + +func f() { + var x []byte + x++ // ERROR "invalid operation: x[+][+]" + +} -- cgit v1.3 From 870f4e190ccbc70900bd0ba724d4f4c1e14e3070 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 25 Sep 2014 13:24:43 -0400 Subject: cmd/gc: emit error for out-of-bounds slice of constant string Fixes #7200. LGTM=gri, iant R=golang-codereviews, gri, iant CC=golang-codereviews, r https://golang.org/cl/150020044 --- src/cmd/gc/typecheck.c | 25 +++++++++++++++---------- test/fixedbugs/issue4232.go | 29 ++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 9440526060..ff49fe6f92 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -33,7 +33,7 @@ static void stringtoarraylit(Node**); static Node* resolve(Node*); static void checkdefergo(Node*); static int checkmake(Type*, char*, Node*); -static int checksliceindex(Node*, Type*); +static int checksliceindex(Node*, Node*, Type*); static int checksliceconst(Node*, Node*); static NodeList* typecheckdefstack; @@ -311,6 +311,7 @@ typecheck1(Node **np, int top) Type *t, *tp, *missing, *have, *badtype; Val v; char *why, *desc, descbuf[64]; + vlong x; n = *np; @@ -895,11 +896,12 @@ reswitch: break; } if(isconst(n->right, CTINT)) { - if(mpgetfix(n->right->val.u.xval) < 0) + x = mpgetfix(n->right->val.u.xval); + if(x < 0) yyerror("invalid %s index %N (index must be non-negative)", why, n->right); - else if(isfixedarray(t) && t->bound > 0 && mpgetfix(n->right->val.u.xval) >= t->bound) + else if(isfixedarray(t) && t->bound > 0 && x >= t->bound) yyerror("invalid array index %N (out of bounds for %d-element array)", n->right, t->bound); - else if(isconst(n->left, CTSTR) && mpgetfix(n->right->val.u.xval) >= n->left->val.u.sval->len) + else if(isconst(n->left, CTSTR) && x >= n->left->val.u.sval->len) yyerror("invalid string index %N (out of bounds for %d-byte string)", n->right, n->left->val.u.sval->len); else if(mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0) yyerror("invalid %s index %N (index too large)", why, n->right); @@ -999,9 +1001,9 @@ reswitch: yyerror("cannot slice %N (type %T)", l, t); goto error; } - if((lo = n->right->left) != N && checksliceindex(lo, tp) < 0) + if((lo = n->right->left) != N && checksliceindex(l, lo, tp) < 0) goto error; - if((hi = n->right->right) != N && checksliceindex(hi, tp) < 0) + if((hi = n->right->right) != N && checksliceindex(l, hi, tp) < 0) goto error; if(checksliceconst(lo, hi) < 0) goto error; @@ -1048,11 +1050,11 @@ reswitch: yyerror("cannot slice %N (type %T)", l, t); goto error; } - if((lo = n->right->left) != N && checksliceindex(lo, tp) < 0) + if((lo = n->right->left) != N && checksliceindex(l, lo, tp) < 0) goto error; - if((mid = n->right->right->left) != N && checksliceindex(mid, tp) < 0) + if((mid = n->right->right->left) != N && checksliceindex(l, mid, tp) < 0) goto error; - if((hi = n->right->right->right) != N && checksliceindex(hi, tp) < 0) + if((hi = n->right->right->right) != N && checksliceindex(l, hi, tp) < 0) goto error; if(checksliceconst(lo, hi) < 0 || checksliceconst(lo, mid) < 0 || checksliceconst(mid, hi) < 0) goto error; @@ -1822,7 +1824,7 @@ out: } static int -checksliceindex(Node *r, Type *tp) +checksliceindex(Node *l, Node *r, Type *tp) { Type *t; @@ -1839,6 +1841,9 @@ checksliceindex(Node *r, Type *tp) } else if(tp != nil && tp->bound > 0 && mpgetfix(r->val.u.xval) > tp->bound) { yyerror("invalid slice index %N (out of bounds for %d-element array)", r, tp->bound); return -1; + } else if(isconst(l, CTSTR) && mpgetfix(r->val.u.xval) > l->val.u.sval->len) { + yyerror("invalid slice index %N (out of bounds for %d-byte string)", r, l->val.u.sval->len); + return -1; } else if(mpcmpfixfix(r->val.u.xval, maxintval[TINT]) > 0) { yyerror("invalid slice index %N (index too large)", r); return -1; diff --git a/test/fixedbugs/issue4232.go b/test/fixedbugs/issue4232.go index e5daa65623..755b1b1de0 100644 --- a/test/fixedbugs/issue4232.go +++ b/test/fixedbugs/issue4232.go @@ -4,6 +4,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// issue 4232 +// issue 7200 + package p func f() { @@ -12,22 +15,42 @@ func f() { _ = a[-1:] // ERROR "invalid slice index -1|index out of bounds" _ = a[:-1] // ERROR "invalid slice index -1|index out of bounds" _ = a[10] // ERROR "invalid array index 10|index out of bounds" + _ = a[9:10] + _ = a[10:10] + _ = a[9:12] // ERROR "invalid slice index 12|index out of bounds" + _ = a[11:12] // ERROR "invalid slice index 11|index out of bounds" + _ = a[1<<100 : 1<<110] // ERROR "overflows int" "invalid slice index 1 << 100|index out of bounds" var s []int _ = s[-1] // ERROR "invalid slice index -1|index out of bounds" _ = s[-1:] // ERROR "invalid slice index -1|index out of bounds" _ = s[:-1] // ERROR "invalid slice index -1|index out of bounds" _ = s[10] + _ = s[9:10] + _ = s[10:10] + _ = s[9:12] + _ = s[11:12] + _ = s[1<<100 : 1<<110] // ERROR "overflows int" "invalid slice index 1 << 100|index out of bounds" - const c = "foo" + const c = "foofoofoof" _ = c[-1] // ERROR "invalid string index -1|index out of bounds" _ = c[-1:] // ERROR "invalid slice index -1|index out of bounds" _ = c[:-1] // ERROR "invalid slice index -1|index out of bounds" - _ = c[3] // ERROR "invalid string index 3|index out of bounds" + _ = c[10] // ERROR "invalid string index 10|index out of bounds" + _ = c[9:10] + _ = c[10:10] + _ = c[9:12] // ERROR "invalid slice index 12|index out of bounds" + _ = c[11:12] // ERROR "invalid slice index 11|index out of bounds" + _ = c[1<<100 : 1<<110] // ERROR "overflows int" "invalid slice index 1 << 100|index out of bounds" var t string _ = t[-1] // ERROR "invalid string index -1|index out of bounds" _ = t[-1:] // ERROR "invalid slice index -1|index out of bounds" _ = t[:-1] // ERROR "invalid slice index -1|index out of bounds" - _ = t[3] + _ = t[10] + _ = t[9:10] + _ = t[10:10] + _ = t[9:12] + _ = t[11:12] + _ = t[1<<100 : 1<<110] // ERROR "overflows int" "invalid slice index 1 << 100|index out of bounds" } -- cgit v1.3 From cfae41ff36d833719b073d1eec5f0fd535ca9e61 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Thu, 25 Sep 2014 10:52:02 -0700 Subject: time: make it clear that time.Time values do not compare with == LGTM=bradfitz, josharian, adg, rsc R=golang-codereviews, bradfitz, josharian, rsc, adg CC=golang-codereviews https://golang.org/cl/141340043 --- src/time/time.go | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/time/time.go b/src/time/time.go index 4f76d79ee5..0300e846a4 100644 --- a/src/time/time.go +++ b/src/time/time.go @@ -31,6 +31,11 @@ import "errors" // change the instant in time being denoted and therefore does not affect the // computations described in earlier paragraphs. // +// Note that the Go == operator compares not just the time instant but also the +// Location. Therefore, Time values should not be used as map or database keys +// without first guaranteeing that the identical Location has been set for all +// values, which can be achieved through use of the UTC or Local method. +// type Time struct { // sec gives the number of seconds elapsed since // January 1, year 1 00:00:00 UTC. -- cgit v1.3 From 74c0de8fb6ef26deece0541a7bf9337ce30c1878 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Thu, 25 Sep 2014 12:45:21 -0700 Subject: bufio: fix handling of empty tokens at end of line/file Fixes #8672. LGTM=rsc R=golang-codereviews, rsc CC=golang-codereviews https://golang.org/cl/145390043 --- src/bufio/scan.go | 4 +++- src/bufio/scan_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/bufio/scan.go b/src/bufio/scan.go index c5714f331e..a41451524d 100644 --- a/src/bufio/scan.go +++ b/src/bufio/scan.go @@ -112,7 +112,9 @@ func (s *Scanner) Scan() bool { // Loop until we have a token. for { // See if we can get a token with what we already have. - if s.end > s.start { + // If we've run out of data but have an error, give the split function + // a chance to recover any remaining, possibly empty token. + if s.end > s.start || s.err != nil { advance, token, err := s.split(s.buf[s.start:s.end], s.err != nil) if err != nil { s.setErr(err) diff --git a/src/bufio/scan_test.go b/src/bufio/scan_test.go index 3ddb25acf9..1454a8113c 100644 --- a/src/bufio/scan_test.go +++ b/src/bufio/scan_test.go @@ -419,3 +419,39 @@ func TestScanWordsExcessiveWhiteSpace(t *testing.T) { t.Fatalf("unexpected token: %v", token) } } + +// Test that empty tokens, including at end of line or end of file, are found by the scanner. +// Issue 8672: Could miss final empty token. + +func commaSplit(data []byte, atEOF bool) (advance int, token []byte, err error) { + for i := 0; i < len(data); i++ { + if data[i] == ',' { + return i + 1, data[:i], nil + } + } + if !atEOF { + return 0, nil, nil + } + return 0, data, nil +} + +func TestEmptyTokens(t *testing.T) { + s := NewScanner(strings.NewReader("1,2,3,")) + values := []string{"1", "2", "3", ""} + s.Split(commaSplit) + var i int + for i = 0; i < len(values); i++ { + if !s.Scan() { + break + } + if s.Text() != values[i] { + t.Errorf("%d: expected %q got %q", i, values[i], s.Text()) + } + } + if i != len(values) { + t.Errorf("got %d fields, expected %d", i, len(values)) + } + if err := s.Err(); err != nil { + t.Fatal(err) + } +} -- cgit v1.3 From dd84cf4ea0aefe7758dea21a6d123f8283941bf9 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 25 Sep 2014 15:57:52 -0400 Subject: cmd/go: install dependencies for 'go build -i' on a command Fixes #8242. LGTM=r R=golang-codereviews, r CC=golang-codereviews, iant https://golang.org/cl/147150043 --- src/cmd/go/build.go | 11 ++--- src/cmd/go/test.bash | 114 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 78 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index fcc6b699be..c72631ae9e 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -284,6 +284,11 @@ func runBuild(cmd *Command, args []string) { } } + depMode := modeBuild + if buildI { + depMode = modeInstall + } + if *buildO != "" { if len(pkgs) > 1 { fatalf("go build: cannot use -o with multiple packages") @@ -292,17 +297,13 @@ func runBuild(cmd *Command, args []string) { } p := pkgs[0] p.target = "" // must build - not up to date - a := b.action(modeInstall, modeBuild, p) + a := b.action(modeInstall, depMode, p) a.target = *buildO b.do(a) return } a := &action{} - depMode := modeBuild - if buildI { - depMode = modeInstall - } for _, p := range packages(args) { a.deps = append(a.deps, b.action(modeBuild, depMode, p)) } diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 9ae17e1054..8bd01ea21b 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -11,11 +11,15 @@ go() { } started=false +testdesc="" +nl=" +" TEST() { if $started; then stop fi echo TEST: "$@" + testdesc="$@" started=true ok=true } @@ -29,6 +33,7 @@ stop() { echo PASS else echo FAIL + testfail="$testfail $testdesc$nl" allok=false fi } @@ -59,8 +64,8 @@ rm -r $d testlocal() { local="$1" TEST local imports $2 '(easy)' - ./testgo build -o hello "testdata/$local/easy.go" - ./hello >hello.out + ./testgo build -o hello "testdata/$local/easy.go" || ok=false + ./hello >hello.out || ok=false if ! grep -q '^easysub\.Hello' hello.out; then echo "testdata/$local/easy.go did not generate expected output" cat hello.out @@ -68,8 +73,8 @@ testlocal() { fi TEST local imports $2 '(easysub)' - ./testgo build -o hello "testdata/$local/easysub/main.go" - ./hello >hello.out + ./testgo build -o hello "testdata/$local/easysub/main.go" || ok=false + ./hello >hello.out || ok=false if ! grep -q '^easysub\.Hello' hello.out; then echo "testdata/$local/easysub/main.go did not generate expected output" cat hello.out @@ -77,8 +82,8 @@ testlocal() { fi TEST local imports $2 '(hard)' - ./testgo build -o hello "testdata/$local/hard.go" - ./hello >hello.out + ./testgo build -o hello "testdata/$local/hard.go" || ok=false + ./hello >hello.out || ok=false if ! grep -q '^sub\.Hello' hello.out || ! grep -q '^subsub\.Hello' hello.out ; then echo "testdata/$local/hard.go did not generate expected output" cat hello.out @@ -317,20 +322,20 @@ TEST godoc installs into GOBIN d=$(mktemp -d -t testgoXXX) export GOPATH=$d mkdir $d/gobin -GOBIN=$d/gobin ./testgo get code.google.com/p/go.tools/cmd/godoc +GOBIN=$d/gobin ./testgo get code.google.com/p/go.tools/cmd/godoc || ok=false if [ ! -x $d/gobin/godoc ]; then echo did not install godoc to '$GOBIN' - GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc + GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc || true ok=false fi TEST godoc installs into GOROOT GOROOT=$(./testgo env GOROOT) rm -f $GOROOT/bin/godoc -./testgo install code.google.com/p/go.tools/cmd/godoc +./testgo install code.google.com/p/go.tools/cmd/godoc || ok=false if [ ! -x $GOROOT/bin/godoc ]; then echo did not install godoc to '$GOROOT/bin' - ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc + ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc || true ok=false fi @@ -338,36 +343,36 @@ TEST cmd/fix installs into tool GOOS=$(./testgo env GOOS) GOARCH=$(./testgo env GOARCH) rm -f $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix -./testgo install cmd/fix +./testgo install cmd/fix || ok=false if [ ! -x $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix ]; then echo 'did not install cmd/fix to $GOROOT/pkg/tool' - GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix + GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix || true ok=false fi rm -f $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix -GOBIN=$d/gobin ./testgo install cmd/fix +GOBIN=$d/gobin ./testgo install cmd/fix || ok=false if [ ! -x $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix ]; then echo 'did not install cmd/fix to $GOROOT/pkg/tool with $GOBIN set' - GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix + GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix || true ok=false fi TEST gopath program installs into GOBIN mkdir $d/src/progname echo 'package main; func main() {}' >$d/src/progname/p.go -GOBIN=$d/gobin ./testgo install progname +GOBIN=$d/gobin ./testgo install progname || ok=false if [ ! -x $d/gobin/progname ]; then echo 'did not install progname to $GOBIN/progname' - ./testgo list -f 'Target: {{.Target}}' cmd/api + ./testgo list -f 'Target: {{.Target}}' cmd/api || true ok=false fi rm -f $d/gobin/progname $d/bin/progname TEST gopath program installs into GOPATH/bin -./testgo install progname +./testgo install progname || ok=false if [ ! -x $d/bin/progname ]; then echo 'did not install progname to $GOPATH/bin/progname' - ./testgo list -f 'Target: {{.Target}}' progname + ./testgo list -f 'Target: {{.Target}}' progname || true ok=false fi @@ -396,7 +401,7 @@ fi # ensure that output of 'go list' is consistent between runs TEST go list is consistent -./testgo list std > test_std.list +./testgo list std > test_std.list || ok=false if ! ./testgo list std | cmp -s test_std.list - ; then echo "go list std ordering is inconsistent" ok=false @@ -470,7 +475,7 @@ func main() { println(extern) } EOF -./testgo run -ldflags '-X main.extern "hello world"' $d/main.go 2>hello.out +./testgo run -ldflags '-X main.extern "hello world"' $d/main.go 2>hello.out || ok=false if ! grep -q '^hello world' hello.out; then echo "ldflags -X main.extern 'hello world' failed. Output:" cat hello.out @@ -608,28 +613,36 @@ TEST shadowing logic export GOPATH=$(pwd)/testdata/shadow/root1:$(pwd)/testdata/shadow/root2 # The math in root1 is not "math" because the standard math is. +set +e cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root1/src/math) +set -e if [ "$cdir" != "(_$(pwd)/testdata/shadow/root1/src/math) ($GOROOT/src/math)" ]; then echo shadowed math is not shadowed: "$cdir" ok=false fi # The foo in root1 is "foo". +set +e cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root1/src/foo) +set -e if [ "$cdir" != "(foo) ()" ]; then echo unshadowed foo is shadowed: "$cdir" ok=false fi # The foo in root2 is not "foo" because the foo in root1 got there first. +set +e cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root2/src/foo) +set -e if [ "$cdir" != "(_$(pwd)/testdata/shadow/root2/src/foo) ($(pwd)/testdata/shadow/root1/src/foo)" ]; then echo shadowed foo is not shadowed: "$cdir" ok=false fi # The error for go install should mention the conflicting directory. -err=$(! ./testgo install ./testdata/shadow/root2/src/foo 2>&1) +set +e +err=$(./testgo install ./testdata/shadow/root2/src/foo 2>&1) +set -e if [ "$err" != "go install: no install location for $(pwd)/testdata/shadow/root2/src/foo: hidden by $(pwd)/testdata/shadow/root1/src/foo" ]; then echo wrong shadowed install error: "$err" ok=false @@ -818,30 +831,46 @@ echo ' package foo func F() {} ' >$d/src/x/y/foo/foo.go +checkbar() { + desc="$1" + sleep 1 + touch $d/src/x/y/foo/foo.go + if ! ./testgo build -v -i x/y/bar &> $d/err; then + echo build -i "$1" failed + cat $d/err + ok=false + elif ! grep x/y/foo $d/err >/dev/null; then + echo first build -i "$1" did not build x/y/foo + cat $d/err + ok=false + fi + if ! ./testgo build -v -i x/y/bar &> $d/err; then + echo second build -i "$1" failed + cat $d/err + ok=false + elif grep x/y/foo $d/err >/dev/null; then + echo second build -i "$1" built x/y/foo + cat $d/err + ok=false + fi +} + echo ' package bar import "x/y/foo" func F() { foo.F() } ' >$d/src/x/y/bar/bar.go -if ! ./testgo build -v -i x/y/bar &> $d/err; then - echo build -i failed - cat $d/err - ok=false -elif ! grep x/y/foo $d/err >/dev/null; then - echo first build -i did not build x/y/foo - cat $d/err - ok=false -fi -if ! ./testgo build -v -i x/y/bar &> $d/err; then - echo second build -i failed - cat $d/err - ok=false -elif grep x/y/foo $d/err >/dev/null; then - echo second build -i built x/y/foo - cat $d/err - ok=false -fi -rm -rf $d +checkbar pkg + +TEST build -i installs dependencies for command +echo ' +package main +import "x/y/foo" +func main() { foo.F() } +' >$d/src/x/y/bar/bar.go +checkbar cmd + +rm -rf $d bar unset GOPATH TEST 'go build in test-only directory fails with a good error' @@ -876,7 +905,7 @@ fi TEST 'go test xtestonly works' export GOPATH=$(pwd)/testdata -./testgo clean -i xtestonly +./testgo clean -i xtestonly || ok=false if ! ./testgo test xtestonly >/dev/null; then echo "go test xtestonly failed" ok=false @@ -927,6 +956,7 @@ rm -f testgo if $allok; then echo PASS else - echo FAIL + echo FAIL: + echo "$testfail" exit 1 fi -- cgit v1.3 From 9c3fc838ba982571e704c1674e9f97678f8a6e93 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Thu, 25 Sep 2014 15:18:25 -0700 Subject: encoding/gob: error rather than panic when decoding enormous slices Fixes #8084. LGTM=ruiu R=golang-codereviews, ruiu CC=golang-codereviews https://golang.org/cl/142710043 --- src/encoding/gob/decode.go | 12 +++++++++++- src/encoding/gob/decoder.go | 9 ++++++--- src/encoding/gob/encoder_test.go | 22 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/encoding/gob/decode.go b/src/encoding/gob/decode.go index 2367650c8b..502209a8a8 100644 --- a/src/encoding/gob/decode.go +++ b/src/encoding/gob/decode.go @@ -312,6 +312,9 @@ func decUint8Slice(i *decInstr, state *decoderState, value reflect.Value) { if n > state.b.Len() { errorf("%s data too long for buffer: %d", value.Type(), n) } + if n > tooBig { + errorf("byte slice too big: %d", n) + } if value.Cap() < n { value.Set(reflect.MakeSlice(value.Type(), n, n)) } else { @@ -539,8 +542,15 @@ func (dec *Decoder) decodeSlice(state *decoderState, value reflect.Value, elemOp // of interfaces, there will be buffer reloads. errorf("length of %s is negative (%d bytes)", value.Type(), u) } + typ := value.Type() + size := uint64(typ.Elem().Size()) + // Take care with overflow in this calculation. + nBytes := u * size + if nBytes > tooBig || (size > 0 && nBytes/size != u) { + errorf("%s slice too big: %d elements of %d bytes", typ.Elem(), n, size) + } if value.Cap() < n { - value.Set(reflect.MakeSlice(value.Type(), n, n)) + value.Set(reflect.MakeSlice(typ, n, n)) } else { value.Set(value.Slice(0, n)) } diff --git a/src/encoding/gob/decoder.go b/src/encoding/gob/decoder.go index 3a769ec125..dcad7a0e48 100644 --- a/src/encoding/gob/decoder.go +++ b/src/encoding/gob/decoder.go @@ -13,6 +13,11 @@ import ( "sync" ) +// tooBig provides a sanity check for sizes; used in several places. +// Upper limit of 1GB, allowing room to grow a little without overflow. +// TODO: make this adjustable? +const tooBig = 1 << 30 + // A Decoder manages the receipt of type and data information read from the // remote side of a connection. type Decoder struct { @@ -75,9 +80,7 @@ func (dec *Decoder) recvMessage() bool { dec.err = err return false } - // Upper limit of 1GB, allowing room to grow a little without overflow. - // TODO: We might want more control over this limit. - if nbytes >= 1<<30 { + if nbytes >= tooBig { dec.err = errBadCount return false } diff --git a/src/encoding/gob/encoder_test.go b/src/encoding/gob/encoder_test.go index 376df82f15..0ea4c0ec8e 100644 --- a/src/encoding/gob/encoder_test.go +++ b/src/encoding/gob/encoder_test.go @@ -932,3 +932,25 @@ func Test29ElementSlice(t *testing.T) { return } } + +// Don't crash, just give error when allocating a huge slice. +// Issue 8084. +func TestErrorForHugeSlice(t *testing.T) { + // Encode an int slice. + buf := new(bytes.Buffer) + slice := []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + err := NewEncoder(buf).Encode(slice) + if err != nil { + t.Fatal("encode:", err) + } + // Reach into the buffer and smash the count to make the encoded slice very long. + buf.Bytes()[buf.Len()-len(slice)-1] = 0xfa + // Decode and see error. + err = NewDecoder(buf).Decode(&slice) + if err == nil { + t.Fatal("decode: no error") + } + if !strings.Contains(err.Error(), "slice too big") { + t.Fatal("decode: expected slice too big error, got %s", err.Error()) + } +} -- cgit v1.3 From 868948badf4a033678c7d9510543b747c2157b11 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Thu, 25 Sep 2014 17:16:27 -0700 Subject: encoding/gob: fix 386 build LGTM=ruiu R=golang-codereviews, ruiu CC=golang-codereviews https://golang.org/cl/146320043 --- src/encoding/gob/decode.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/encoding/gob/decode.go b/src/encoding/gob/decode.go index 502209a8a8..6a9213fb3c 100644 --- a/src/encoding/gob/decode.go +++ b/src/encoding/gob/decode.go @@ -536,18 +536,15 @@ func (dec *Decoder) ignoreMap(state *decoderState, keyOp, elemOp decOp) { // Slices are encoded as an unsigned length followed by the elements. func (dec *Decoder) decodeSlice(state *decoderState, value reflect.Value, elemOp decOp, ovfl error) { u := state.decodeUint() - n := int(u) - if n < 0 || uint64(n) != u { - // We don't check n against buffer length here because if it's a slice - // of interfaces, there will be buffer reloads. - errorf("length of %s is negative (%d bytes)", value.Type(), u) - } typ := value.Type() size := uint64(typ.Elem().Size()) - // Take care with overflow in this calculation. nBytes := u * size - if nBytes > tooBig || (size > 0 && nBytes/size != u) { - errorf("%s slice too big: %d elements of %d bytes", typ.Elem(), n, size) + n := int(u) + // Take care with overflow in this calculation. + if n < 0 || uint64(n) != u || nBytes > tooBig || (size > 0 && nBytes/size != u) { + // We don't check n against buffer length here because if it's a slice + // of interfaces, there will be buffer reloads. + errorf("%s slice too big: %d elements of %d bytes", typ.Elem(), u, size) } if value.Cap() < n { value.Set(reflect.MakeSlice(typ, n, n)) -- cgit v1.3 From 64df53ed7f46db4a404812e2ef347521dc95a039 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Fri, 26 Sep 2014 11:02:09 +1000 Subject: crypto/tls: ensure that we don't resume when tickets are disabled. LGTM=r R=r, adg, rsc https://golang.org/cl/148080043 --- src/crypto/tls/handshake_server.go | 4 + src/crypto/tls/handshake_server_test.go | 26 +++++++ .../testdata/Server-TLSv12-IssueTicketPreDisable | 87 ++++++++++++++++++++++ .../tls/testdata/Server-TLSv12-ResumeDisabled | 87 ++++++++++++++++++++++ src/crypto/tls/ticket.go | 3 +- 5 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 src/crypto/tls/testdata/Server-TLSv12-IssueTicketPreDisable create mode 100644 src/crypto/tls/testdata/Server-TLSv12-ResumeDisabled (limited to 'src') diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go index 684ab288f0..520675dfb5 100644 --- a/src/crypto/tls/handshake_server.go +++ b/src/crypto/tls/handshake_server.go @@ -231,6 +231,10 @@ Curves: func (hs *serverHandshakeState) checkForResumption() bool { c := hs.c + if c.config.SessionTicketsDisabled { + return false + } + var ok bool if hs.sessionState, ok = c.decryptTicket(hs.clientHello.sessionTicket); !ok { return false diff --git a/src/crypto/tls/handshake_server_test.go b/src/crypto/tls/handshake_server_test.go index 36c79a9b0c..580fbc0bfb 100644 --- a/src/crypto/tls/handshake_server_test.go +++ b/src/crypto/tls/handshake_server_test.go @@ -676,6 +676,32 @@ func TestResumption(t *testing.T) { runServerTestTLS12(t, test) } +func TestResumptionDisabled(t *testing.T) { + sessionFilePath := tempFile("") + defer os.Remove(sessionFilePath) + + config := *testConfig + + test := &serverTest{ + name: "IssueTicketPreDisable", + command: []string{"openssl", "s_client", "-cipher", "RC4-SHA", "-sess_out", sessionFilePath}, + config: &config, + } + runServerTestTLS12(t, test) + + config.SessionTicketsDisabled = true + + test = &serverTest{ + name: "ResumeDisabled", + command: []string{"openssl", "s_client", "-cipher", "RC4-SHA", "-sess_in", sessionFilePath}, + config: &config, + } + runServerTestTLS12(t, test) + + // One needs to manually confirm that the handshake in the golden data + // file for ResumeDisabled does not include a resumption handshake. +} + // cert.pem and key.pem were generated with generate_cert.go // Thus, they have no ExtKeyUsage fields and trigger an error // when verification is turned on. diff --git a/src/crypto/tls/testdata/Server-TLSv12-IssueTicketPreDisable b/src/crypto/tls/testdata/Server-TLSv12-IssueTicketPreDisable new file mode 100644 index 0000000000..30f0026815 --- /dev/null +++ b/src/crypto/tls/testdata/Server-TLSv12-IssueTicketPreDisable @@ -0,0 +1,87 @@ +>>> Flow 1 (client to server) +00000000 16 03 01 00 60 01 00 00 5c 03 03 54 23 54 02 17 |....`...\..T#T..| +00000010 f3 53 13 3d 48 88 c3 19 b9 d1 3d 33 7f f5 99 56 |.S.=H.....=3...V| +00000020 04 71 1b d9 d5 64 8a 0d 4a 54 00 00 00 04 00 05 |.q...d..JT......| +00000030 00 ff 01 00 00 2f 00 23 00 00 00 0d 00 22 00 20 |...../.#.....". | +00000040 06 01 06 02 06 03 05 01 05 02 05 03 04 01 04 02 |................| +00000050 04 03 03 01 03 02 03 03 02 01 02 02 02 03 01 01 |................| +00000060 00 0f 00 01 01 |.....| +>>> Flow 2 (server to client) +00000000 16 03 03 00 35 02 00 00 31 03 03 00 00 00 00 00 |....5...1.......| +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00 |................| +00000030 09 00 23 00 00 ff 01 00 01 00 16 03 03 02 be 0b |..#.............| +00000040 00 02 ba 00 02 b7 00 02 b4 30 82 02 b0 30 82 02 |.........0...0..| +00000050 19 a0 03 02 01 02 02 09 00 85 b0 bb a4 8a 7f b8 |................| +00000060 ca 30 0d 06 09 2a 86 48 86 f7 0d 01 01 05 05 00 |.0...*.H........| +00000070 30 45 31 0b 30 09 06 03 55 04 06 13 02 41 55 31 |0E1.0...U....AU1| +00000080 13 30 11 06 03 55 04 08 13 0a 53 6f 6d 65 2d 53 |.0...U....Some-S| +00000090 74 61 74 65 31 21 30 1f 06 03 55 04 0a 13 18 49 |tate1!0...U....I| +000000a0 6e 74 65 72 6e 65 74 20 57 69 64 67 69 74 73 20 |nternet Widgits | +000000b0 50 74 79 20 4c 74 64 30 1e 17 0d 31 30 30 34 32 |Pty Ltd0...10042| +000000c0 34 30 39 30 39 33 38 5a 17 0d 31 31 30 34 32 34 |4090938Z..110424| +000000d0 30 39 30 39 33 38 5a 30 45 31 0b 30 09 06 03 55 |090938Z0E1.0...U| +000000e0 04 06 13 02 41 55 31 13 30 11 06 03 55 04 08 13 |....AU1.0...U...| +000000f0 0a 53 6f 6d 65 2d 53 74 61 74 65 31 21 30 1f 06 |.Some-State1!0..| +00000100 03 55 04 0a 13 18 49 6e 74 65 72 6e 65 74 20 57 |.U....Internet W| +00000110 69 64 67 69 74 73 20 50 74 79 20 4c 74 64 30 81 |idgits Pty Ltd0.| +00000120 9f 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 |.0...*.H........| +00000130 03 81 8d 00 30 81 89 02 81 81 00 bb 79 d6 f5 17 |....0.......y...| +00000140 b5 e5 bf 46 10 d0 dc 69 be e6 2b 07 43 5a d0 03 |...F...i..+.CZ..| +00000150 2d 8a 7a 43 85 b7 14 52 e7 a5 65 4c 2c 78 b8 23 |-.zC...R..eL,x.#| +00000160 8c b5 b4 82 e5 de 1f 95 3b 7e 62 a5 2c a5 33 d6 |........;~b.,.3.| +00000170 fe 12 5c 7a 56 fc f5 06 bf fa 58 7b 26 3f b5 cd |..\zV.....X{&?..| +00000180 04 d3 d0 c9 21 96 4a c7 f4 54 9f 5a bf ef 42 71 |....!.J..T.Z..Bq| +00000190 00 fe 18 99 07 7f 7e 88 7d 7d f1 04 39 c4 a2 2e |......~.}}..9...| +000001a0 db 51 c9 7c e3 c0 4c 3b 32 66 01 cf af b1 1d b8 |.Q.|..L;2f......| +000001b0 71 9a 1d db db 89 6b ae da 2d 79 02 03 01 00 01 |q.....k..-y.....| +000001c0 a3 81 a7 30 81 a4 30 1d 06 03 55 1d 0e 04 16 04 |...0..0...U.....| +000001d0 14 b1 ad e2 85 5a cf cb 28 db 69 ce 23 69 de d3 |.....Z..(.i.#i..| +000001e0 26 8e 18 88 39 30 75 06 03 55 1d 23 04 6e 30 6c |&...90u..U.#.n0l| +000001f0 80 14 b1 ad e2 85 5a cf cb 28 db 69 ce 23 69 de |......Z..(.i.#i.| +00000200 d3 26 8e 18 88 39 a1 49 a4 47 30 45 31 0b 30 09 |.&...9.I.G0E1.0.| +00000210 06 03 55 04 06 13 02 41 55 31 13 30 11 06 03 55 |..U....AU1.0...U| +00000220 04 08 13 0a 53 6f 6d 65 2d 53 74 61 74 65 31 21 |....Some-State1!| +00000230 30 1f 06 03 55 04 0a 13 18 49 6e 74 65 72 6e 65 |0...U....Interne| +00000240 74 20 57 69 64 67 69 74 73 20 50 74 79 20 4c 74 |t Widgits Pty Lt| +00000250 64 82 09 00 85 b0 bb a4 8a 7f b8 ca 30 0c 06 03 |d...........0...| +00000260 55 1d 13 04 05 30 03 01 01 ff 30 0d 06 09 2a 86 |U....0....0...*.| +00000270 48 86 f7 0d 01 01 05 05 00 03 81 81 00 08 6c 45 |H.............lE| +00000280 24 c7 6b b1 59 ab 0c 52 cc f2 b0 14 d7 87 9d 7a |$.k.Y..R.......z| +00000290 64 75 b5 5a 95 66 e4 c5 2b 8e ae 12 66 1f eb 4f |du.Z.f..+...f..O| +000002a0 38 b3 6e 60 d3 92 fd f7 41 08 b5 25 13 b1 18 7a |8.n`....A..%...z| +000002b0 24 fb 30 1d ba ed 98 b9 17 ec e7 d7 31 59 db 95 |$.0.........1Y..| +000002c0 d3 1d 78 ea 50 56 5c d5 82 5a 2d 5a 5f 33 c4 b6 |..x.PV\..Z-Z_3..| +000002d0 d8 c9 75 90 96 8c 0f 52 98 b5 cd 98 1f 89 20 5f |..u....R...... _| +000002e0 f2 a0 1c a3 1b 96 94 dd a9 fd 57 e9 70 e8 26 6d |..........W.p.&m| +000002f0 71 99 9b 26 6e 38 50 29 6c 90 a7 bd d9 16 03 03 |q..&n8P)l.......| +00000300 00 04 0e 00 00 00 |......| +>>> Flow 3 (client to server) +00000000 16 03 03 00 86 10 00 00 82 00 80 27 e9 a4 f7 e7 |...........'....| +00000010 df 25 de 84 8c 1f d6 e6 c3 11 28 55 9a c1 91 37 |.%........(U...7| +00000020 84 f5 ba f8 80 0d ca 50 cb 1e 72 f7 97 6f c2 b2 |.......P..r..o..| +00000030 04 4d 13 7c e0 6e a0 1f 91 e1 38 1b a2 c0 55 16 |.M.|.n....8...U.| +00000040 7f 29 fc ed 1c 1a cf 72 14 c3 00 c1 dd 36 36 af |.).....r.....66.| +00000050 a6 e4 a8 be ba ec 13 d0 1e d0 1d fd e1 5b 27 fd |.............['.| +00000060 9a da 2e 12 c8 b0 b9 c2 b9 76 ec 7f 3c 98 b6 63 |.........v..<..c| +00000070 bc da f0 07 7a 3d e7 61 f4 2f 12 80 3b f9 3b cc |....z=.a./..;.;.| +00000080 05 c8 2f 7e 28 b2 73 bf 97 61 29 14 03 03 00 01 |../~(.s..a).....| +00000090 01 16 03 03 00 24 17 59 a9 45 53 46 33 96 50 dd |.....$.Y.ESF3.P.| +000000a0 3e 23 aa 91 38 f8 56 4a 2f 1a f2 b1 44 9b ce 17 |>#..8.VJ/...D...| +000000b0 6b 8a 89 76 bc 67 b8 8b ba 90 |k..v.g....| +>>> Flow 4 (server to client) +00000000 16 03 03 00 72 04 00 00 6e 00 00 00 00 00 68 00 |....r...n.....h.| +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 65 |...............e| +00000020 ea 4b d1 ef ba 2d db 0c ba 9a d4 20 76 57 c8 ec |.K...-..... vW..| +00000030 dc 2d 77 fb fb 3b 93 5f 53 e0 14 4f 90 fb d6 55 |.-w..;._S..O...U| +00000040 57 8c 8d 0d 25 ea 5d 0d f2 91 e5 12 22 12 ec 7b |W...%.]....."..{| +00000050 5f b6 6e fd 07 59 23 24 fc b1 97 ca ea 56 a5 c2 |_.n..Y#$.....V..| +00000060 a0 e4 9e 99 64 f2 64 d0 75 7a 46 63 e3 dc 21 ed |....d.d.uzFc..!.| +00000070 78 56 e9 e1 ab 66 80 14 03 03 00 01 01 16 03 03 |xV...f..........| +00000080 00 24 fc 14 68 07 17 1f df b7 84 cb fd c1 e0 e4 |.$..h...........| +00000090 f2 1a ea 34 b5 00 7f 70 be c8 1c 0a d6 55 e3 57 |...4...p.....U.W| +000000a0 50 4e 6d 7d 8a 5d 17 03 03 00 21 24 27 50 40 c1 |PNm}.]....!$'P@.| +000000b0 c5 bd c7 9f 95 d9 ba 2e 7b 0e db ea a7 31 81 05 |........{....1..| +000000c0 75 43 b1 63 cf b8 55 92 ef 76 98 a9 15 03 03 00 |uC.c..U..v......| +000000d0 16 d7 ea 3c 79 e7 a6 2f 61 39 ec 4e 95 86 48 5e |...>> Flow 1 (client to server) +00000000 16 03 01 00 e8 01 00 00 e4 03 03 54 23 54 02 a5 |...........T#T..| +00000010 10 11 0f 6d e5 2d 2f e8 bb 52 b1 38 3f 65 01 43 |...m.-/..R.8?e.C| +00000020 36 cc 48 f6 09 22 a1 85 20 28 3c 20 35 8b fe 7a |6.H..".. (< 5..z| +00000030 41 3b 59 3a 5d b9 b3 21 f0 62 e9 0d 7b af f5 5d |A;Y:]..!.b..{..]| +00000040 fa 65 1a 40 c8 ca cd 74 8c ef d2 fb 00 04 00 05 |.e.@...t........| +00000050 00 ff 01 00 00 97 00 23 00 68 00 00 00 00 00 00 |.......#.h......| +00000060 00 00 00 00 00 00 00 00 00 00 65 ea 4b d1 ef ba |..........e.K...| +00000070 2d db 0c ba 9a d4 20 76 57 c8 ec dc 2d 77 fb fb |-..... vW...-w..| +00000080 3b 93 5f 53 e0 14 4f 90 fb d6 55 57 8c 8d 0d 25 |;._S..O...UW...%| +00000090 ea 5d 0d f2 91 e5 12 22 12 ec 7b 5f b6 6e fd 07 |.]....."..{_.n..| +000000a0 59 23 24 fc b1 97 ca ea 56 a5 c2 a0 e4 9e 99 64 |Y#$.....V......d| +000000b0 f2 64 d0 75 7a 46 63 e3 dc 21 ed 78 56 e9 e1 ab |.d.uzFc..!.xV...| +000000c0 66 80 00 0d 00 22 00 20 06 01 06 02 06 03 05 01 |f....". ........| +000000d0 05 02 05 03 04 01 04 02 04 03 03 01 03 02 03 03 |................| +000000e0 02 01 02 02 02 03 01 01 00 0f 00 01 01 |.............| +>>> Flow 2 (server to client) +00000000 16 03 03 00 31 02 00 00 2d 03 03 00 00 00 00 00 |....1...-.......| +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00 |................| +00000030 05 ff 01 00 01 00 16 03 03 02 be 0b 00 02 ba 00 |................| +00000040 02 b7 00 02 b4 30 82 02 b0 30 82 02 19 a0 03 02 |.....0...0......| +00000050 01 02 02 09 00 85 b0 bb a4 8a 7f b8 ca 30 0d 06 |.............0..| +00000060 09 2a 86 48 86 f7 0d 01 01 05 05 00 30 45 31 0b |.*.H........0E1.| +00000070 30 09 06 03 55 04 06 13 02 41 55 31 13 30 11 06 |0...U....AU1.0..| +00000080 03 55 04 08 13 0a 53 6f 6d 65 2d 53 74 61 74 65 |.U....Some-State| +00000090 31 21 30 1f 06 03 55 04 0a 13 18 49 6e 74 65 72 |1!0...U....Inter| +000000a0 6e 65 74 20 57 69 64 67 69 74 73 20 50 74 79 20 |net Widgits Pty | +000000b0 4c 74 64 30 1e 17 0d 31 30 30 34 32 34 30 39 30 |Ltd0...100424090| +000000c0 39 33 38 5a 17 0d 31 31 30 34 32 34 30 39 30 39 |938Z..1104240909| +000000d0 33 38 5a 30 45 31 0b 30 09 06 03 55 04 06 13 02 |38Z0E1.0...U....| +000000e0 41 55 31 13 30 11 06 03 55 04 08 13 0a 53 6f 6d |AU1.0...U....Som| +000000f0 65 2d 53 74 61 74 65 31 21 30 1f 06 03 55 04 0a |e-State1!0...U..| +00000100 13 18 49 6e 74 65 72 6e 65 74 20 57 69 64 67 69 |..Internet Widgi| +00000110 74 73 20 50 74 79 20 4c 74 64 30 81 9f 30 0d 06 |ts Pty Ltd0..0..| +00000120 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 81 8d 00 |.*.H............| +00000130 30 81 89 02 81 81 00 bb 79 d6 f5 17 b5 e5 bf 46 |0.......y......F| +00000140 10 d0 dc 69 be e6 2b 07 43 5a d0 03 2d 8a 7a 43 |...i..+.CZ..-.zC| +00000150 85 b7 14 52 e7 a5 65 4c 2c 78 b8 23 8c b5 b4 82 |...R..eL,x.#....| +00000160 e5 de 1f 95 3b 7e 62 a5 2c a5 33 d6 fe 12 5c 7a |....;~b.,.3...\z| +00000170 56 fc f5 06 bf fa 58 7b 26 3f b5 cd 04 d3 d0 c9 |V.....X{&?......| +00000180 21 96 4a c7 f4 54 9f 5a bf ef 42 71 00 fe 18 99 |!.J..T.Z..Bq....| +00000190 07 7f 7e 88 7d 7d f1 04 39 c4 a2 2e db 51 c9 7c |..~.}}..9....Q.|| +000001a0 e3 c0 4c 3b 32 66 01 cf af b1 1d b8 71 9a 1d db |..L;2f......q...| +000001b0 db 89 6b ae da 2d 79 02 03 01 00 01 a3 81 a7 30 |..k..-y........0| +000001c0 81 a4 30 1d 06 03 55 1d 0e 04 16 04 14 b1 ad e2 |..0...U.........| +000001d0 85 5a cf cb 28 db 69 ce 23 69 de d3 26 8e 18 88 |.Z..(.i.#i..&...| +000001e0 39 30 75 06 03 55 1d 23 04 6e 30 6c 80 14 b1 ad |90u..U.#.n0l....| +000001f0 e2 85 5a cf cb 28 db 69 ce 23 69 de d3 26 8e 18 |..Z..(.i.#i..&..| +00000200 88 39 a1 49 a4 47 30 45 31 0b 30 09 06 03 55 04 |.9.I.G0E1.0...U.| +00000210 06 13 02 41 55 31 13 30 11 06 03 55 04 08 13 0a |...AU1.0...U....| +00000220 53 6f 6d 65 2d 53 74 61 74 65 31 21 30 1f 06 03 |Some-State1!0...| +00000230 55 04 0a 13 18 49 6e 74 65 72 6e 65 74 20 57 69 |U....Internet Wi| +00000240 64 67 69 74 73 20 50 74 79 20 4c 74 64 82 09 00 |dgits Pty Ltd...| +00000250 85 b0 bb a4 8a 7f b8 ca 30 0c 06 03 55 1d 13 04 |........0...U...| +00000260 05 30 03 01 01 ff 30 0d 06 09 2a 86 48 86 f7 0d |.0....0...*.H...| +00000270 01 01 05 05 00 03 81 81 00 08 6c 45 24 c7 6b b1 |..........lE$.k.| +00000280 59 ab 0c 52 cc f2 b0 14 d7 87 9d 7a 64 75 b5 5a |Y..R.......zdu.Z| +00000290 95 66 e4 c5 2b 8e ae 12 66 1f eb 4f 38 b3 6e 60 |.f..+...f..O8.n`| +000002a0 d3 92 fd f7 41 08 b5 25 13 b1 18 7a 24 fb 30 1d |....A..%...z$.0.| +000002b0 ba ed 98 b9 17 ec e7 d7 31 59 db 95 d3 1d 78 ea |........1Y....x.| +000002c0 50 56 5c d5 82 5a 2d 5a 5f 33 c4 b6 d8 c9 75 90 |PV\..Z-Z_3....u.| +000002d0 96 8c 0f 52 98 b5 cd 98 1f 89 20 5f f2 a0 1c a3 |...R...... _....| +000002e0 1b 96 94 dd a9 fd 57 e9 70 e8 26 6d 71 99 9b 26 |......W.p.&mq..&| +000002f0 6e 38 50 29 6c 90 a7 bd d9 16 03 03 00 04 0e 00 |n8P)l...........| +00000300 00 00 |..| +>>> Flow 3 (client to server) +00000000 16 03 03 00 86 10 00 00 82 00 80 ae 02 dd 1f 1a |................| +00000010 86 83 f5 2f 82 46 4b 29 58 aa a1 b3 56 8b 4e 40 |.../.FK)X...V.N@| +00000020 ef 23 65 67 ad 48 e5 e1 fd ae dd bf 68 fd bd a6 |.#eg.H......h...| +00000030 13 a0 7e 05 ab f7 20 e1 6a 4e d1 37 93 08 1d c9 |..~... .jN.7....| +00000040 37 e0 b5 34 28 bf 20 45 45 da 0f 7e 51 a7 c6 ae |7..4(. EE..~Q...| +00000050 61 6c 07 1b 73 ef da 6e 25 c4 ed be e3 3f da ae |al..s..n%....?..| +00000060 cd 3c 17 9c 2e ee fb 47 9d b3 a1 b2 c3 5d e0 83 |.<.....G.....]..| +00000070 74 20 37 2d 72 d6 d0 4d 58 0e 26 1c 50 22 95 08 |t 7-r..MX.&.P"..| +00000080 7d e0 5f 86 99 9e 2c 2e a7 a0 7f 14 03 03 00 01 |}._...,.........| +00000090 01 16 03 03 00 24 a2 ab 41 25 a5 cf 04 18 1d 98 |.....$..A%......| +000000a0 88 6c 59 21 86 33 54 f4 35 b4 21 6e a5 29 d5 6e |.lY!.3T.5.!n.).n| +000000b0 3d 08 72 b0 af 46 b5 8f 6b 86 |=.r..F..k.| +>>> Flow 4 (server to client) +00000000 14 03 03 00 01 01 16 03 03 00 24 59 20 4d c2 17 |..........$Y M..| +00000010 8b 3c 9b 33 d9 f9 ef fb 80 18 1f 67 a7 58 12 89 |.<.3.......g.X..| +00000020 4e 73 0f 2d 7b e6 c4 a6 79 73 01 da 22 e8 54 17 |Ns.-{...ys..".T.| +00000030 03 03 00 21 36 ca 64 0f 4a 12 a5 50 3d 97 bb 39 |...!6.d.J..P=..9| +00000040 02 fc ed d1 82 6a 9a 2e 21 79 f6 e1 b3 cc 32 db |.....j..!y....2.| +00000050 0f 5d b3 fb a5 15 03 03 00 16 51 f4 be 57 7a df |.]........Q..Wz.| +00000060 f1 f2 bd b5 51 5e 45 80 be 0b 9a 0c d1 19 3c 79 |....Q^E....... Date: Fri, 26 Sep 2014 12:09:27 -0400 Subject: cmd/go: fix 'go get vanity/repo/...' in clean GOPATH The pattern was only working if the checkout had already been done, but the code was trying to make it work even the first time. Test and fix. Fixes #8335. LGTM=r R=golang-codereviews, r CC=golang-codereviews, iant https://golang.org/cl/146310043 --- src/cmd/go/test.bash | 12 ++++++++++++ src/cmd/go/vcs.go | 9 ++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 8bd01ea21b..5e4e43722a 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -948,6 +948,18 @@ elif ! grep "$GOARCH test3.go p xyzp/test3.go/123" testdata/std.out > /dev/null; ok=false fi +TEST go get works with vanity wildcards +d=$(mktemp -d -t testgoXXX) +export GOPATH=$d +if ! ./testgo get -u rsc.io/pdf/...; then + ok=false +elif [ ! -x $d/bin/pdfpasswd ]; then + echo did not build rsc.io/pdf/pdfpasswd + ok=false +fi +unset GOPATH +rm -rf $d + # clean up if $started; then stop; fi rm -rf testdata/bin testdata/bin1 diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go index d07948e64c..c5d246835d 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -361,7 +361,14 @@ var httpPrefixRE = regexp.MustCompile(`^https?:`) func repoRootForImportPath(importPath string) (*repoRoot, error) { rr, err := repoRootForImportPathStatic(importPath, "") if err == errUnknownSite { - rr, err = repoRootForImportDynamic(importPath) + // If there are wildcards, look up the thing before the wildcard, + // hoping it applies to the wildcarded parts too. + // This makes 'go get rsc.io/pdf/...' work in a fresh GOPATH. + lookup := strings.TrimSuffix(importPath, "/...") + if i := strings.Index(lookup, "/.../"); i >= 0 { + lookup = lookup[:i] + } + rr, err = repoRootForImportDynamic(lookup) // repoRootForImportDynamic returns error detail // that is irrelevant if the user didn't intend to use a -- cgit v1.3 From f3a98dee27540afab414d3201aff18f30c2b163e Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 12:10:13 -0400 Subject: cmd/go: re-resolve and check vcs roots during go get -u If you do 'go get -u rsc.io/pdf' and then rsc.io/pdf's redirect changes to point somewhere else, after this CL a later 'go get -u rsc.io/pdf' will tell you that. Fixes #8548. LGTM=iant R=golang-codereviews, iant CC=adg, golang-codereviews, n13m3y3r, r https://golang.org/cl/147170043 --- src/cmd/go/get.go | 12 ++++++++ src/cmd/go/test.bash | 50 +++++++++++++++++++++++++++++++++ src/cmd/go/vcs.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 135 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go index a34286f540..2640339414 100644 --- a/src/cmd/go/get.go +++ b/src/cmd/go/get.go @@ -266,6 +266,18 @@ func downloadPackage(p *Package) error { return err } repo = "" // should be unused; make distinctive + + // Double-check where it came from. + if *getU && vcs.remoteRepo != nil { + dir := filepath.Join(p.build.SrcRoot, rootPath) + if remote, err := vcs.remoteRepo(vcs, dir); err == nil { + if rr, err := repoRootForImportPath(p.ImportPath); err == nil { + if remote != rr.repo { + return fmt.Errorf("%s is from %s, should be from %s", dir, remote, rr.repo) + } + } + } + } } else { // Analyze the import path to determine the version control system, // repository, and the import path for the root of the repository. diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 5e4e43722a..243467ba9d 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -126,6 +126,56 @@ if ! ./testgo build -v ./testdata/testinternal2; then ok=false fi +# Test that 'go get -u' reports moved packages. +testmove() { + vcs=$1 + url=$2 + base=$3 + config=$4 + + TEST go get -u notices $vcs package that moved + d=$(mktemp -d -t testgoXXX) + mkdir -p $d/src + if ! GOPATH=$d ./testgo get -d $url; then + echo 'go get -d $url failed' + ok=false + elif ! GOPATH=$d ./testgo get -d -u $url; then + echo 'go get -d -u $url failed' + ok=false + else + set +e + case "$vcs" in + svn) + # SVN doesn't believe in text files so we can't just edit the config. + # Check out a different repo into the wrong place. + rm -rf $d/src/code.google.com/p/rsc-svn + GOPATH=$d ./testgo get -d -u code.google.com/p/rsc-svn2/trunk + mv $d/src/code.google.com/p/rsc-svn2 $d/src/code.google.com/p/rsc-svn + ;; + *) + echo '1,$s;'"$base"';'"$base"'XXX; +w +q' | ed $d/src/$config >/dev/null 2>&1 + esac + set -e + + if GOPATH=$d ./testgo get -d -u $url 2>$d/err; then + echo "go get -d -u $url succeeded with wrong remote repo" + cat $d/err + ok=false + elif ! grep 'should be from' $d/err >/dev/null; then + echo "go get -d -u $url failed for wrong reason" + cat $d/err + ok=false + fi + fi + rm -rf $d +} + +testmove hg rsc.io/x86/x86asm x86 rsc.io/x86/.hg/hgrc +testmove git rsc.io/pdf pdf rsc.io/pdf/.git/config +testmove svn code.google.com/p/rsc-svn/trunk - - + export GOPATH=$(pwd)/testdata/importcom TEST 'import comment - match' if ! ./testgo build ./testdata/importcom/works.go; then diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go index c5d246835d..103b67b827 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -33,6 +33,8 @@ type vcsCmd struct { scheme []string pingCmd string + + remoteRepo func(v *vcsCmd, rootDir string) (remoteRepo string, err error) } // A tagCmd describes a command to list available tags @@ -81,8 +83,17 @@ var vcsHg = &vcsCmd{ tagSyncCmd: "update -r {tag}", tagSyncDefault: "update default", - scheme: []string{"https", "http", "ssh"}, - pingCmd: "identify {scheme}://{repo}", + scheme: []string{"https", "http", "ssh"}, + pingCmd: "identify {scheme}://{repo}", + remoteRepo: hgRemoteRepo, +} + +func hgRemoteRepo(vcsHg *vcsCmd, rootDir string) (remoteRepo string, err error) { + out, err := vcsHg.runOutput(rootDir, "paths default") + if err != nil { + return "", err + } + return strings.TrimSpace(string(out)), nil } // vcsGit describes how to use Git. @@ -104,8 +115,38 @@ var vcsGit = &vcsCmd{ tagSyncCmd: "checkout {tag}", tagSyncDefault: "checkout master", - scheme: []string{"git", "https", "http", "git+ssh"}, - pingCmd: "ls-remote {scheme}://{repo}", + scheme: []string{"git", "https", "http", "git+ssh"}, + pingCmd: "ls-remote {scheme}://{repo}", + remoteRepo: gitRemoteRepo, +} + +func gitRemoteRepo(vcsGit *vcsCmd, rootDir string) (remoteRepo string, err error) { + outb, err := vcsGit.runOutput(rootDir, "remote -v") + if err != nil { + return "", err + } + out := string(outb) + + // Expect: + // origin https://github.com/rsc/pdf (fetch) + // origin https://github.com/rsc/pdf (push) + // use first line only. + + if !strings.HasPrefix(out, "origin\t") { + return "", fmt.Errorf("unable to parse output of git remote -v") + } + out = strings.TrimPrefix(out, "origin\t") + i := strings.Index(out, "\n") + if i < 0 { + return "", fmt.Errorf("unable to parse output of git remote -v") + } + out = out[:i] + i = strings.LastIndex(out, " ") + if i < 0 { + return "", fmt.Errorf("unable to parse output of git remote -v") + } + out = out[:i] + return strings.TrimSpace(string(out)), nil } // vcsBzr describes how to use Bazaar. @@ -138,8 +179,34 @@ var vcsSvn = &vcsCmd{ // There is no tag command in subversion. // The branch information is all in the path names. - scheme: []string{"https", "http", "svn", "svn+ssh"}, - pingCmd: "info {scheme}://{repo}", + scheme: []string{"https", "http", "svn", "svn+ssh"}, + pingCmd: "info {scheme}://{repo}", + remoteRepo: svnRemoteRepo, +} + +func svnRemoteRepo(vcsSvn *vcsCmd, rootDir string) (remoteRepo string, err error) { + outb, err := vcsSvn.runOutput(rootDir, "info") + if err != nil { + return "", err + } + out := string(outb) + + // Expect: + // ... + // Repository Root: + // ... + + i := strings.Index(out, "\nRepository Root: ") + if i < 0 { + return "", fmt.Errorf("unable to parse output of svn info") + } + out = out[i+len("\nRepository Root: "):] + i = strings.Index(out, "\n") + if i < 0 { + return "", fmt.Errorf("unable to parse output of svn info") + } + out = out[:i] + return strings.TrimSpace(string(out)), nil } func (v *vcsCmd) String() string { -- cgit v1.3 From b86105e80da7fd994656f134d006a700fe037fc2 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 13:47:51 -0400 Subject: cmd/go: make malformed import path message more precise If you say 'go get -v' you get extra information when import paths are not of the expected form. If you say 'go get -v src/rsc.io/pdf' the message says that src/rsc.io/pdf does not contain a hostname, which is incorrect. The problem is that it does not begin with a hostname. Fixes #7432. LGTM=r R=golang-codereviews, r CC=bradfitz, golang-codereviews, iant https://golang.org/cl/144650043 --- src/cmd/go/vcs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go index 103b67b827..0834a7d192 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -539,11 +539,11 @@ func repoRootForImportPathStatic(importPath, scheme string) (*repoRoot, error) { func repoRootForImportDynamic(importPath string) (*repoRoot, error) { slash := strings.Index(importPath, "/") if slash < 0 { - return nil, errors.New("import path doesn't contain a slash") + return nil, errors.New("import path does not contain a slash") } host := importPath[:slash] if !strings.Contains(host, ".") { - return nil, errors.New("import path doesn't contain a hostname") + return nil, errors.New("import path does not begin with hostname") } urlStr, body, err := httpsOrHTTP(importPath) if err != nil { -- cgit v1.3 From 13a2c1ca78c47a4314e32badddf6c31c4229da7f Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 13:48:30 -0400 Subject: cmd/go: display program name when reporting crash Fix by atom (from CL 89190044), comment and test by me. Fixes #6823. LGTM=crawshaw R=golang-codereviews, crawshaw CC=0xe2.0x9a.0x9b, adg, golang-codereviews, iant, r https://golang.org/cl/148180043 --- src/cmd/go/build.go | 8 ++++++++ src/cmd/go/test.bash | 11 +++++++++++ src/cmd/ld/pobj.c | 9 +++++++++ 3 files changed, 28 insertions(+) (limited to 'src') diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index c72631ae9e..e2e17fd036 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -1469,6 +1469,14 @@ func (b *builder) runOut(dir string, desc string, env []string, cmdargs ...inter continue } + // err can be something like 'exit status 1'. + // Add information about what program was running. + // Note that if buf.Bytes() is non-empty, the caller usually + // shows buf.Bytes() and does not print err at all, so the + // prefix here does not make most output any more verbose. + if err != nil { + err = errors.New(cmdline[0] + ": " + err.Error()) + } return buf.Bytes(), err } } diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 243467ba9d..80af61ae0e 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -60,6 +60,17 @@ if ! grep -q "^$fn:" $d/err.out; then fi rm -r $d +TEST 'program name in crash messages' +linker=$(./testgo env GOCHAR)l +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) +./testgo build -ldflags -crash_for_testing $(./testgo env GOROOT)/test/helloworld.go 2>$d/err.out || true +if ! grep -q "/tool/.*/$linker" $d/err.out; then + echo "missing linker name in error message" + cat $d/err.out + ok=false +fi +rm -r $d + # Test local (./) imports. testlocal() { local="$1" diff --git a/src/cmd/ld/pobj.c b/src/cmd/ld/pobj.c index 54c5ef2472..63460df30a 100644 --- a/src/cmd/ld/pobj.c +++ b/src/cmd/ld/pobj.c @@ -45,6 +45,8 @@ char* paramspace = "FP"; void main(int argc, char *argv[]) { + int i; + linkarchinit(); ctxt = linknew(thelinkarch); ctxt->thechar = thechar; @@ -64,6 +66,13 @@ main(int argc, char *argv[]) INITENTRY = 0; linkmode = LinkAuto; + // For testing behavior of go command when tools crash. + // Undocumented, not in standard flag parser to avoid + // exposing in usage message. + for(i=1; igoarm == 5) debug['F'] = 1; -- cgit v1.3 From ce143f25e625ce40f4655185372dc93661545df0 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 13:50:02 -0400 Subject: cmd/go: add test -o flag to control where test binary is written While we are here, remove undocumented, meaningless test -file flag. Fixes #7724. LGTM=r R=golang-codereviews, r CC=golang-codereviews, iant https://golang.org/cl/149070043 --- src/cmd/go/test.bash | 24 +++++++++++++++++++++++ src/cmd/go/test.go | 52 +++++++++++++++++++++++++++++++++----------------- src/cmd/go/testflag.go | 7 ++++--- 3 files changed, 63 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 80af61ae0e..bc2ce710a0 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -552,6 +552,30 @@ if [ ! -x strings.test ]; then fi rm -f strings.prof strings.test +TEST go test -cpuprofile -o controls binary location +./testgo test -cpuprofile strings.prof -o mystrings.test strings || ok=false +if [ ! -x mystrings.test ]; then + echo "go test -cpuprofile -o mystrings.test did not create mystrings.test" + ok=false +fi +rm -f strings.prof mystrings.test + +TEST go test -c -o controls binary location +./testgo test -c -o mystrings.test strings || ok=false +if [ ! -x mystrings.test ]; then + echo "go test -c -o mystrings.test did not create mystrings.test" + ok=false +fi +rm -f mystrings.test + +TEST go test -o writes binary +./testgo test -o mystrings.test strings || ok=false +if [ ! -x mystrings.test ]; then + echo "go test -o mystrings.test did not create mystrings.test" + ok=false +fi +rm -f mystrings.test + TEST symlinks do not confuse go list '(issue 4568)' old=$(pwd) tmp=$(cd /tmp && pwd -P) diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index c135b89c84..100ef5fa82 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -66,16 +66,23 @@ non-test installation. In addition to the build flags, the flags handled by 'go test' itself are: - -c Compile the test binary to pkg.test but do not run it. - (Where pkg is the last element of the package's import path.) + -c + Compile the test binary to pkg.test but do not run it + (where pkg is the last element of the package's import path). + The file name can be changed with the -o flag. + + -exec xprog + Run the test binary using xprog. The behavior is the same as + in 'go run'. See 'go help run' for details. -i Install packages that are dependencies of the test. Do not run the test. - -exec xprog - Run the test binary using xprog. The behavior is the same as - in 'go run'. See 'go help run' for details. + -o file + Compile the test binary to the named file. + The test still runs (unless -c or -i is specified). + The test binary also accepts flags that control execution of the test; these flags are also accessible by 'go test'. See 'go help testflag' for details. @@ -123,6 +130,7 @@ control the execution of any test: -blockprofile block.out Write a goroutine blocking profile to the specified file when all tests are complete. + Writes test binary as -c would. -blockprofilerate n Control the detail provided in goroutine blocking profiles by @@ -154,8 +162,7 @@ control the execution of any test: Sets -cover. -coverprofile cover.out - Write a coverage profile to the specified file after all tests - have passed. + Write a coverage profile to the file after all tests have passed. Sets -cover. -cpu 1,2,4 @@ -165,10 +172,11 @@ control the execution of any test: -cpuprofile cpu.out Write a CPU profile to the specified file before exiting. + Writes test binary as -c would. -memprofile mem.out - Write a memory profile to the specified file after all tests - have passed. + Write a memory profile to the file after all tests have passed. + Writes test binary as -c would. -memprofilerate n Enable more precise (and expensive) memory profiles by setting @@ -275,10 +283,10 @@ var ( testCoverMode string // -covermode flag testCoverPaths []string // -coverpkg flag testCoverPkgs []*Package // -coverpkg flag + testO string // -o flag testProfile bool // some profiling flag testNeedBinary bool // profile needs to keep binary around testV bool // -v flag - testFiles []string // -file flag(s) TODO: not respected testTimeout string // -timeout flag testArgs []string testBench bool @@ -310,6 +318,9 @@ func runTest(cmd *Command, args []string) { if testC && len(pkgs) != 1 { fatalf("cannot use -c flag with multiple packages") } + if testO != "" && len(pkgs) != 1 { + fatalf("cannot use -o flag with multiple packages") + } if testProfile && len(pkgs) != 1 { fatalf("cannot use test profile flag with multiple packages") } @@ -781,17 +792,24 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, a.objdir = testDir + string(filepath.Separator) a.objpkg = filepath.Join(testDir, "main.a") a.target = filepath.Join(testDir, testBinary) + exeSuffix - pmainAction := a + buildAction = a if testC || testNeedBinary { // -c or profiling flag: create action to copy binary to ./test.out. - runAction = &action{ + target := filepath.Join(cwd, testBinary+exeSuffix) + if testO != "" { + target = testO + if !filepath.IsAbs(target) { + target = filepath.Join(cwd, target) + } + } + buildAction = &action{ f: (*builder).install, - deps: []*action{pmainAction}, + deps: []*action{buildAction}, p: pmain, - target: filepath.Join(cwd, testBinary+exeSuffix), + target: target, } - pmainAction = runAction // in case we are running the test + runAction = buildAction // make sure runAction != nil even if not running test } if testC { printAction = &action{p: p, deps: []*action{runAction}} // nop @@ -799,7 +817,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, // run test runAction = &action{ f: (*builder).runTest, - deps: []*action{pmainAction}, + deps: []*action{buildAction}, p: p, ignoreFail: true, } @@ -815,7 +833,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, } } - return pmainAction, runAction, printAction, nil + return buildAction, runAction, printAction, nil } func testImportStack(top string, p *Package, target string) []string { diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go index 73f311e5f6..6da74b9967 100644 --- a/src/cmd/go/testflag.go +++ b/src/cmd/go/testflag.go @@ -65,9 +65,9 @@ type testFlagSpec struct { var testFlagDefn = []*testFlagSpec{ // local. {name: "c", boolVar: &testC}, - {name: "file", multiOK: true}, {name: "cover", boolVar: &testCover}, {name: "coverpkg"}, + {name: "o"}, // build flags. {name: "a", boolVar: &buildA}, @@ -153,6 +153,9 @@ func testFlags(args []string) (packageNames, passToTest []string) { // bool flags. case "a", "c", "i", "n", "x", "v", "race", "cover", "work": setBoolFlag(f.boolVar, value) + case "o": + testO = value + testNeedBinary = true case "p": setIntFlag(&buildP, value) case "exec": @@ -184,8 +187,6 @@ func testFlags(args []string) (packageNames, passToTest []string) { buildContext.BuildTags = strings.Fields(value) case "compiler": buildCompiler{}.Set(value) - case "file": - testFiles = append(testFiles, value) case "bench": // record that we saw the flag; don't care about the value testBench = true -- cgit v1.3 From 6d760fb082544531bd14be27b438a1d3a1ec0016 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 13:50:39 -0400 Subject: cmd/go: document that testdata directories are ignored Also rebuild doc.go; was stale, so contains extra changes. Fixes #8677. LGTM=r R=golang-codereviews, r CC=golang-codereviews, iant https://golang.org/cl/148170043 --- src/cmd/go/doc.go | 29 +++++++++++++++++++---------- src/cmd/go/help.go | 3 ++- 2 files changed, 21 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go index 0d4e263891..cffb53d995 100644 --- a/src/cmd/go/doc.go +++ b/src/cmd/go/doc.go @@ -524,16 +524,23 @@ non-test installation. In addition to the build flags, the flags handled by 'go test' itself are: - -c Compile the test binary to pkg.test but do not run it. - (Where pkg is the last element of the package's import path.) + -c + Compile the test binary to pkg.test but do not run it + (where pkg is the last element of the package's import path). + The file name can be changed with the -o flag. + + -exec xprog + Run the test binary using xprog. The behavior is the same as + in 'go run'. See 'go help run' for details. -i Install packages that are dependencies of the test. Do not run the test. - -exec xprog - Run the test binary using xprog. The behavior is the same as - in 'go run'. See 'go help run' for details. + -o file + Compile the test binary to the named file. + The test still runs (unless -c or -i is specified). + The test binary also accepts flags that control execution of the test; these flags are also accessible by 'go test'. See 'go help testflag' for details. @@ -910,7 +917,8 @@ single directory, the command is applied to a single synthesized package made up of exactly those files, ignoring any build constraints in those files and ignoring any other files in the directory. -File names that begin with "." or "_" are ignored by the go tool. +Directory and file names that begin with "." or "_" are ignored +by the go tool, as are directories named "testdata". Description of testing flags @@ -942,6 +950,7 @@ control the execution of any test: -blockprofile block.out Write a goroutine blocking profile to the specified file when all tests are complete. + Writes test binary as -c would. -blockprofilerate n Control the detail provided in goroutine blocking profiles by @@ -973,8 +982,7 @@ control the execution of any test: Sets -cover. -coverprofile cover.out - Write a coverage profile to the specified file after all tests - have passed. + Write a coverage profile to the file after all tests have passed. Sets -cover. -cpu 1,2,4 @@ -984,10 +992,11 @@ control the execution of any test: -cpuprofile cpu.out Write a CPU profile to the specified file before exiting. + Writes test binary as -c would. -memprofile mem.out - Write a memory profile to the specified file after all tests - have passed. + Write a memory profile to the file after all tests have passed. + Writes test binary as -c would. -memprofilerate n Enable more precise (and expensive) memory profiles by setting diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go index d6651d179b..201f0e2d79 100644 --- a/src/cmd/go/help.go +++ b/src/cmd/go/help.go @@ -81,7 +81,8 @@ single directory, the command is applied to a single synthesized package made up of exactly those files, ignoring any build constraints in those files and ignoring any other files in the directory. -File names that begin with "." or "_" are ignored by the go tool. +Directory and file names that begin with "." or "_" are ignored +by the go tool, as are directories named "testdata". `, } -- cgit v1.3 From df781cc4abf83225ec2e0dbd6e16dc8dd6cab36d Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 13:50:53 -0400 Subject: liblink: fix cmd/ld -X flag This fixes the test/linkx.go test, which does not run by default. (Issue 4139 is about fixing that.) Fixes #8806. LGTM=r R=golang-codereviews, r CC=bradfitz, golang-codereviews, iant https://golang.org/cl/145420043 --- src/liblink/objfile.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/liblink/objfile.c b/src/liblink/objfile.c index 15d602df92..b2478ec178 100644 --- a/src/liblink/objfile.c +++ b/src/liblink/objfile.c @@ -589,6 +589,8 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn) typ = rdsym(ctxt, f, pkg); if(typ != nil) // if bss sym defined multiple times, take type from any one def s->gotype = typ; + if(dup != nil && typ != nil) + dup->gotype = typ; rddata(f, &s->p, &s->np); s->maxp = s->np; n = rdint(f); -- cgit v1.3 From 0b8bc7cee9cc8c2bedc030e9a59a687201210212 Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Fri, 26 Sep 2014 14:36:49 -0400 Subject: cmd/go: handle paths like \x.go on windows Fixes #8130. LGTM=rsc R=golang-codereviews, rsc CC=golang-codereviews https://golang.org/cl/143200043 --- src/cmd/go/build.go | 9 +++++-- src/cmd/go/go_windows_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 src/cmd/go/go_windows_test.go (limited to 'src') diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index e2e17fd036..23ad765ba7 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -505,8 +505,13 @@ func goFilesPackage(gofiles []string) *Package { } ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil } - if !filepath.IsAbs(dir) { - dir = filepath.Join(cwd, dir) + var err error + if dir == "" { + dir = cwd + } + dir, err = filepath.Abs(dir) + if err != nil { + fatalf("%s", err) } bp, err := ctxt.ImportDir(dir, 0) diff --git a/src/cmd/go/go_windows_test.go b/src/cmd/go/go_windows_test.go new file mode 100644 index 0000000000..53d695cccc --- /dev/null +++ b/src/cmd/go/go_windows_test.go @@ -0,0 +1,55 @@ +// Copyright 2014 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 ( + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +func TestAbsolutePath(t *testing.T) { + tmp, err := ioutil.TempDir("", "TestAbsolutePath") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmp) + + file := filepath.Join(tmp, "a.go") + err = ioutil.WriteFile(file, []byte{}, 0644) + if err != nil { + t.Fatal(err) + } + dir := filepath.Join(tmp, "dir") + err = os.Mkdir(dir, 0777) + if err != nil { + t.Fatal(err) + } + + wd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + defer os.Chdir(wd) + + // Chdir so current directory and a.go reside on the same drive. + err = os.Chdir(dir) + if err != nil { + t.Fatal(err) + } + + noVolume := file[len(filepath.VolumeName(file)):] + wrongPath := filepath.Join(dir, noVolume) + output, err := exec.Command("go", "build", noVolume).CombinedOutput() + if err == nil { + t.Fatal("build should fail") + } + if strings.Contains(string(output), wrongPath) { + t.Fatalf("wrong output found: %v %v", err, string(output)) + } +} -- cgit v1.3 From 8c3005c4929e26cba74c80703123e150fa3fda1a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 14:41:26 -0400 Subject: cmd/go: make build -a skip standard packages in Go releases Today, 'go build -a my/pkg' and 'go install -a my/pkg' recompile not just my/pkg and all its dependencies that you wrote but also the standard library packages. Recompiling the standard library is problematic on some systems because the installed copy is not writable. The -a behavior means that you can't use 'go install -a all' or 'go install -a my/...' to rebuild everything after a Go release - the rebuild stops early when it cannot overwrite the installed standard library. During development work, however, you do want install -a to rebuild everything, because anything might have changed. Resolve the conflict by making the behavior of -a depend on whether we are using a released copy of Go or a devel copy. In the release copies, -a no longer applies to the standard library. In the devel copies, it still does. This is the latest in a long line of refinements to the "do I build this or not" logic. It is surely not the last. Fixes #8290. LGTM=r R=golang-codereviews, r, tracey.brendan CC=adg, golang-codereviews, iant https://golang.org/cl/151730045 --- src/cmd/go/build.go | 1 + src/cmd/go/doc.go | 1 + src/cmd/go/pkg.go | 18 +++++++++++++++++- src/cmd/go/test.bash | 28 +++++++++++++++++++++++++++- src/cmd/go/testgo.go | 21 +++++++++++++++++++++ 5 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/cmd/go/testgo.go (limited to 'src') diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 23ad765ba7..9c7b42650a 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -57,6 +57,7 @@ and test commands: -a force rebuilding of packages that are already up-to-date. + In Go releases, does not apply to the standard library. -n print the commands but do not run them. -p n diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go index cffb53d995..8e2facd044 100644 --- a/src/cmd/go/doc.go +++ b/src/cmd/go/doc.go @@ -76,6 +76,7 @@ and test commands: -a force rebuilding of packages that are already up-to-date. + In Go releases, does not apply to the standard library. -n print the commands but do not run them. -p n diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index 4bbcc2b971..eafaa8ee67 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -14,6 +14,7 @@ import ( "os" pathpkg "path" "path/filepath" + "runtime" "sort" "strings" "time" @@ -685,6 +686,12 @@ func computeStale(pkgs ...*Package) { } } +// The runtime version string takes one of two forms: +// "go1.X[.Y]" for Go releases, and "devel +hash" at tip. +// Determine whether we are in a released copy by +// inspecting the version. +var isGoRelease = !strings.HasPrefix(runtime.Version(), "go1") + // isStale reports whether package p needs to be rebuilt. func isStale(p *Package, topRoot map[string]bool) bool { if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") { @@ -705,7 +712,16 @@ func isStale(p *Package, topRoot map[string]bool) bool { return false } - if buildA || p.target == "" || p.Stale { + // If we are running a release copy of Go, do not rebuild the standard packages. + // They may not be writable anyway, but they are certainly not changing. + // This makes 'go build -a' skip the standard packages when using an official release. + // See issue 4106 and issue 8290. + pkgBuildA := buildA + if p.Standard && isGoRelease { + pkgBuildA = false + } + + if pkgBuildA || p.target == "" || p.Stale { return true } diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index bc2ce710a0..1284876193 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -4,7 +4,7 @@ # license that can be found in the LICENSE file. set -e -go build -o testgo +go build -tags testgo -o testgo go() { echo TEST ERROR: ran go, not testgo: go "$@" >&2 exit 2 @@ -71,6 +71,32 @@ if ! grep -q "/tool/.*/$linker" $d/err.out; then fi rm -r $d +TEST 'go build -a in dev branch' +./testgo install math || ok=false # should be up to date already but just in case +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) +if ! TESTGO_IS_GO_RELEASE=0 ./testgo build -v -a math 2>$d/err.out; then + cat $d/err.out + ok=false +elif ! grep -q runtime $d/err.out; then + echo "testgo build -a math in dev branch DID NOT build runtime, but should have" + cat $d/err.out + ok=false +fi +rm -r $d + +TEST 'go build -a in release branch' +./testgo install math || ok=false # should be up to date already but just in case +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) +if ! TESTGO_IS_GO_RELEASE=1 ./testgo build -v -a math 2>$d/err.out; then + cat $d/err.out + ok=false +elif grep -q runtime $d/err.out; then + echo "testgo build -a math in dev branch DID build runtime, but should NOT have" + cat $d/err.out + ok=false +fi +rm -r $d + # Test local (./) imports. testlocal() { local="$1" diff --git a/src/cmd/go/testgo.go b/src/cmd/go/testgo.go new file mode 100644 index 0000000000..01923f74bd --- /dev/null +++ b/src/cmd/go/testgo.go @@ -0,0 +1,21 @@ +// Copyright 2014 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. + +// This file contains extra hooks for testing the go command. +// It is compiled into the Go binary only when building the +// test copy; it does not get compiled into the standard go +// command, so these testing hooks are not present in the +// go command that everyone uses. + +// +build testgo + +package main + +import "os" + +func init() { + if v := os.Getenv("TESTGO_IS_GO_RELEASE"); v != "" { + isGoRelease = v == "1" + } +} -- cgit v1.3 From 1bf18b42f8475db2af1618d798285ed84a8dd521 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 15:15:48 -0400 Subject: cmd/go: fix -a The one line that you can't test easily was broken. This manifested as a failure of a pre-existing test in test.bash but I didn't notice it (there are a few other long-standing failures that need to be fixed). TBR=r CC=golang-codereviews https://golang.org/cl/146340044 --- src/cmd/go/pkg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index eafaa8ee67..7f7a3b04fd 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -690,7 +690,7 @@ func computeStale(pkgs ...*Package) { // "go1.X[.Y]" for Go releases, and "devel +hash" at tip. // Determine whether we are in a released copy by // inspecting the version. -var isGoRelease = !strings.HasPrefix(runtime.Version(), "go1") +var isGoRelease = strings.HasPrefix(runtime.Version(), "go1") // isStale reports whether package p needs to be rebuilt. func isStale(p *Package, topRoot map[string]bool) bool { -- cgit v1.3 From b2487ef6a399790cfe57127c3f50fc59341460e4 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Fri, 26 Sep 2014 12:33:05 -0700 Subject: flag: allow CommandLine's Usage function to be set Fixes #7779. LGTM=rsc R=golang-codereviews, rsc CC=golang-codereviews https://golang.org/cl/147210043 --- src/flag/flag.go | 15 +++++++++------ src/flag/flag_test.go | 10 ++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/flag/flag.go b/src/flag/flag.go index de2d91f8b1..323e452a83 100644 --- a/src/flag/flag.go +++ b/src/flag/flag.go @@ -406,6 +406,7 @@ func defaultUsage(f *FlagSet) { // for how to write your own usage function. // Usage prints to standard error a usage message documenting all defined command-line flags. +// It is called when an error occurs while parsing flags. // The function is a variable that may be changed to point to a custom function. var Usage = func() { fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) @@ -702,13 +703,15 @@ func (f *FlagSet) failf(format string, a ...interface{}) error { return err } -// usage calls the Usage method for the flag set, or the usage function if -// the flag set is CommandLine. +// usage calls the Usage method for the flag set if one is specified, +// or the appropriate default usage function otherwise. func (f *FlagSet) usage() { - if f == CommandLine { - Usage() - } else if f.Usage == nil { - defaultUsage(f) + if f.Usage == nil { + if f == CommandLine { + Usage() + } else { + defaultUsage(f) + } } else { f.Usage() } diff --git a/src/flag/flag_test.go b/src/flag/flag_test.go index 2c03872697..8c88c8c274 100644 --- a/src/flag/flag_test.go +++ b/src/flag/flag_test.go @@ -251,6 +251,16 @@ func TestUserDefined(t *testing.T) { } } +func TestUserDefinedForCommandLine(t *testing.T) { + const help = "HELP" + var result string + ResetForTesting(func() { result = help }) + Usage() + if result != help { + t.Fatalf("got %q; expected %q", result, help) + } +} + // Declare a user-defined boolean flag type. type boolFlagVar struct { count int -- cgit v1.3 From bfebf9ea8071683af608b8bf291fc7d8365d501b Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 17:03:31 -0400 Subject: cmd/yacc: fix parsing of character tokens From issue 7967 I learned: 1) yacc accepts either 'x' or "x" to mean token value 0x78 2) yacc also accepts 'xyz' and "XYZ" to mean token value 0x78 Use strconv.Unquote to simplify the handling of quoted strings and check that each has only one rune. Although this does clean things up, it makes 'x' and "x" treated as different internally (now they are stored as `'x'` and `"x"`; before they were both ` x`). Grammars that use both interchangeably will now die with an error similar to the one from issue 7967: yacc bug -- cannot have 2 different Ts with same value "+" and '+' The echoing of the quotes should make clear what is going on. The other semantic change caused by using strconv.Unquote is that '\"' and "\'" are no longer valid. Like in Go, they must be spelled without the backslash: '"' and "'". On the other hand, now yacc and Go agree about what character and string literals mean. LGTM=r R=r CC=golang-codereviews https://golang.org/cl/149110043 --- src/cmd/yacc/yacc.go | 76 +++++++++------------------------------------------- 1 file changed, 13 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go index c53403266e..0761811cf4 100644 --- a/src/cmd/yacc/yacc.go +++ b/src/cmd/yacc/yacc.go @@ -52,9 +52,9 @@ import ( "go/format" "io/ioutil" "os" + "strconv" "strings" "unicode" - "unicode/utf8" ) // the following are adjustable @@ -756,64 +756,16 @@ func defin(nt int, s string) int { // establish value for token // single character literal - if s[0] == ' ' { - s = s[1:] - r, size := utf8.DecodeRuneInString(s) - if r == utf8.RuneError && size == 1 { - errorf("invalid UTF-8 sequence %q", s) - } - val = int(r) - if val == '\\' { // escape sequence - switch { - case len(s) == 2: - // single character escape sequence - switch s[1] { - case '\'': - val = '\'' - case '"': - val = '"' - case '\\': - val = '\\' - case 'a': - val = '\a' - case 'b': - val = '\b' - case 'f': - val = '\f' - case 'n': - val = '\n' - case 'r': - val = '\r' - case 't': - val = '\t' - case 'v': - val = '\v' - default: - errorf("invalid escape %s", s) - } - case s[1] == 'u' && len(s) == 2+4, // \unnnn sequence - s[1] == 'U' && len(s) == 2+8: // \Unnnnnnnn sequence - val = 0 - s = s[2:] - for s != "" { - c := int(s[0]) - switch { - case c >= '0' && c <= '9': - c -= '0' - case c >= 'a' && c <= 'f': - c -= 'a' - 10 - case c >= 'A' && c <= 'F': - c -= 'A' - 10 - default: - errorf(`illegal \u or \U construction`) - } - val = val*16 + c - s = s[1:] - } - default: - errorf("invalid escape %s", s) - } + if s[0] == '\'' || s[0] == '"' { + q, err := strconv.Unquote(s) + if err != nil { + errorf("invalid token: %s", err) + } + rq := []rune(q) + if len(rq) != 1 { + errorf("character token too long: %s", s) } + val = int(rq[0]) if val == 0 { errorf("token value 0 is illegal") } @@ -896,7 +848,7 @@ func gettok() int { case '"', '\'': match = c - tokname = " " + tokname = string(c) for { c = getrune(finput) if c == '\n' || c == EOF { @@ -909,6 +861,7 @@ func gettok() int { if tokflag { fmt.Printf(">>> IDENTIFIER \"%v\" %v\n", tokname, lineno) } + tokname += string(c) return IDENTIFIER } tokname += string(c) @@ -1029,7 +982,7 @@ func fdtype(t int) int { } func chfind(t int, s string) int { - if s[0] == ' ' { + if s[0] == '"' || s[0] == '\'' { t = 0 } for i := 0; i <= ntokens; i++ { @@ -1516,9 +1469,6 @@ func symnam(i int) string { } else { s = tokset[i].name } - if s[0] == ' ' { - s = s[1:] - } return s } -- cgit v1.3 From 754cd5419ace8b1cdc615c3fe58febbcec7b61a0 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 17:09:11 -0400 Subject: cmd/go: always build _test.go files and link into test go test's handling of _test.go files when the entire package's set of files has no Test functions has varied over the past few releases. There are a few interesting cases (all contain no Test functions): (1) x_test.go has syntax errors (2) x_test.go has type errors (3) x_test.go has runtime errors (say, a func init that panics) In Go 1.1, tests with (1) or (2) failed; (3) passed. In Go 1.2, tests with (1) or (2) failed; (3) passed. In Go 1.3, tests with (1) failed; (2) or (3) passed. After this CL, tests with (1), (2), or (3) all fail. This is clearly a corner case, but it seems to me that the behavior of the test should not change if you add or remove a line like func TestAlwaysPasses(t *testing.T) {} That implies that the _test.go files must always be built and always be imported into the test binary. Doing so means that (1), (2), and (3) must all fail. Fixes #8337. LGTM=iant R=golang-codereviews, iant CC=adg, golang-codereviews, r https://golang.org/cl/150980043 --- src/cmd/go/test.bash | 14 ++++++++++++++ src/cmd/go/test.go | 6 ++++-- src/cmd/go/testdata/src/badtest/badexec/x_test.go | 5 +++++ src/cmd/go/testdata/src/badtest/badsyntax/x.go | 1 + src/cmd/go/testdata/src/badtest/badsyntax/x_test.go | 3 +++ src/cmd/go/testdata/src/badtest/badvar/x.go | 1 + src/cmd/go/testdata/src/badtest/badvar/x_test.go | 5 +++++ 7 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 src/cmd/go/testdata/src/badtest/badexec/x_test.go create mode 100644 src/cmd/go/testdata/src/badtest/badsyntax/x.go create mode 100644 src/cmd/go/testdata/src/badtest/badsyntax/x_test.go create mode 100644 src/cmd/go/testdata/src/badtest/badvar/x.go create mode 100644 src/cmd/go/testdata/src/badtest/badvar/x_test.go (limited to 'src') diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 1284876193..6a72bcde07 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -71,6 +71,20 @@ if ! grep -q "/tool/.*/$linker" $d/err.out; then fi rm -r $d +TEST broken tests without Test functions all fail +d=$(mktemp -d -t testgoXXX) +./testgo test ./testdata/src/badtest/... >$d/err 2>&1 || true +if grep -q '^ok' $d/err; then + echo test passed unexpectedly: + grep '^ok' $d/err + ok=false +elif ! grep -q 'FAIL.*badtest/badexec' $d/err || ! grep -q 'FAIL.*badtest/badsyntax' $d/err || ! grep -q 'FAIL.*badtest/badvar' $d/err; then + echo test did not run everything + cat $d/err + ok=false +fi +rm -rf $d + TEST 'go build -a in dev branch' ./testgo install math || ok=false # should be up to date already but just in case d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index 100ef5fa82..0962e5bb50 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -736,11 +736,13 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, if err != nil { return nil, nil, nil, err } - if t.ImportTest || ptest.coverMode != "" { + if len(ptest.GoFiles) > 0 { pmain.imports = append(pmain.imports, ptest) + t.ImportTest = true } - if t.ImportXtest { + if pxtest != nil { pmain.imports = append(pmain.imports, pxtest) + t.ImportXtest = true } if ptest != p && localCover { diff --git a/src/cmd/go/testdata/src/badtest/badexec/x_test.go b/src/cmd/go/testdata/src/badtest/badexec/x_test.go new file mode 100644 index 0000000000..12f5051712 --- /dev/null +++ b/src/cmd/go/testdata/src/badtest/badexec/x_test.go @@ -0,0 +1,5 @@ +package badexec + +func init() { + panic("badexec") +} diff --git a/src/cmd/go/testdata/src/badtest/badsyntax/x.go b/src/cmd/go/testdata/src/badtest/badsyntax/x.go new file mode 100644 index 0000000000..c8a5407a5a --- /dev/null +++ b/src/cmd/go/testdata/src/badtest/badsyntax/x.go @@ -0,0 +1 @@ +package badsyntax diff --git a/src/cmd/go/testdata/src/badtest/badsyntax/x_test.go b/src/cmd/go/testdata/src/badtest/badsyntax/x_test.go new file mode 100644 index 0000000000..5be10745d9 --- /dev/null +++ b/src/cmd/go/testdata/src/badtest/badsyntax/x_test.go @@ -0,0 +1,3 @@ +package badsyntax + +func func func func func! diff --git a/src/cmd/go/testdata/src/badtest/badvar/x.go b/src/cmd/go/testdata/src/badtest/badvar/x.go new file mode 100644 index 0000000000..fdd46c4c72 --- /dev/null +++ b/src/cmd/go/testdata/src/badtest/badvar/x.go @@ -0,0 +1 @@ +package badvar diff --git a/src/cmd/go/testdata/src/badtest/badvar/x_test.go b/src/cmd/go/testdata/src/badtest/badvar/x_test.go new file mode 100644 index 0000000000..c67df01c5c --- /dev/null +++ b/src/cmd/go/testdata/src/badtest/badvar/x_test.go @@ -0,0 +1,5 @@ +package badvar_test + +func f() { + _ = notdefined +} -- cgit v1.3 From 4a8cb4a49c8bda0759dd3bc0c9fc6bdf5f6aa6b7 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 17:13:24 -0400 Subject: math: avoid assumption of denormalized math mode in Sincos The extra-clever code in Sincos is trying to do if v&2 == 0 { mask = 0xffffffffffffffff } else { mask = 0 } It does this by turning v&2 into a float64 X0 and then using MOVSD $0.0, X3 CMPSD X0, X3, 0 That CMPSD is defined to behave like: if X0 == X3 { X3 = 0xffffffffffffffff } else { X3 = 0 } which gives the desired mask in X3. The goal in using the CMPSD was to avoid a conditional branch. This code fails when called from a PortAudio callback. In particular, the failure behavior is exactly as if the CMPSD always chose the 'true' execution. Notice that the comparison X0 == X3 is comparing as floating point values the 64-bit pattern v&2 and the actual floating point value zero. The only possible values for v&2 are 0x0000000000000000 (floating point zero) and 0x0000000000000002 (floating point 1e-323, a denormal). If they are both comparing equal to zero, I conclude that in a PortAudio callback (whatever that means), the processor is running in "denormals are zero" mode. I confirmed this by placing the processor into that mode and running the test case in the bug; it produces the incorrect output reported in the bug. In general, if a Go program changes the floating point math modes to something other than what Go expects, the math library is not going to work exactly as intended, so we might be justified in not fixing this at all. However, it seems reasonable that the client code might have expected "denormals are zero" mode to only affect actual processing of denormals. This code has produced what is in effect a gratuitous denormal by being extra clever. There is nothing about the computation being requested that fundamentally requires a denormal. It is also easy to do this computation in integer math instead: mask = ((v&2)>>1)-1 Do that. For the record, the other math tests that fail if you put the processor in "denormals are zero" mode are the tests for Frexp, Ilogb, Ldexp, Logb, Log2, and FloatMinMax, but all fail processing denormal inputs. Sincos was the only function for which that mode causes incorrect behavior on non-denormal inputs. The existing tests check that the new assembly is correct. There is no test for behavior in "denormals are zero" mode, because I don't want to add assembly to change that. Fixes #8623. LGTM=josharian R=golang-codereviews, josharian CC=golang-codereviews, iant, r https://golang.org/cl/151750043 --- src/math/sincos_amd64.s | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/math/sincos_amd64.s b/src/math/sincos_amd64.s index dae636b248..59bf55f58c 100644 --- a/src/math/sincos_amd64.s +++ b/src/math/sincos_amd64.s @@ -15,9 +15,7 @@ // The README file says, "The software is in public domain. // You can use the software without any obligation." // -// This code is a simplified version of the original. The CMPSD -// instruction, not generated by the compiler, eliminates jumps in the -// body of the calculation. +// This code is a simplified version of the original. #define PosOne 0x3FF0000000000000 #define PosInf 0x7FF0000000000000 @@ -96,11 +94,10 @@ TEXT ·Sincos(SB),NOSPLIT,$0 // if ((q + 1) & 2) != 0 { sin, cos = cos, sin } MOVQ $1, DX ADDQ BX, DX - MOVQ $2, AX - ANDQ AX, DX - MOVQ DX, X0 - MOVSD $0.0, X3 - CMPSD X0, X3, 0 // cmpeq; x1= x, x2= z, x3 = y, x7= d, bx= q + ANDQ $2, DX + SHRQ $1, DX + SUBQ $1, DX + MOVQ DX, X3 // sin = (y & z) | (^y & x) MOVAPD X2, X0 ANDPD X3, X0 // x0= sin -- cgit v1.3 From f6fc14094a476d2e23722f124cfcd8204c2659b0 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sun, 28 Sep 2014 08:27:05 -0700 Subject: cmd/ld: don't automatically mark symbols created by -X as reachable This fixes the bug in which the linker reports "missing Go type information" when a -X option refers to a symbol that is not used. Fixes #8821. LGTM=rsc R=rsc, r CC=golang-codereviews https://golang.org/cl/151000043 --- src/cmd/ld/data.c | 9 +++++++-- src/cmd/ld/lib.c | 4 +++- test/linkx.go | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 89226bfe28..9983a9281c 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -620,6 +620,7 @@ addstrdata(char *name, char *value) { LSym *s, *sp; char *p; + uchar reachable; p = smprint("%s.str", name); sp = linklookup(ctxt, p, 0); @@ -630,13 +631,17 @@ addstrdata(char *name, char *value) s = linklookup(ctxt, name, 0); s->size = 0; s->dupok = 1; + reachable = s->reachable; addaddr(ctxt, s, sp); adduint32(ctxt, s, strlen(value)); if(PtrSize == 8) adduint32(ctxt, s, 0); // round struct to pointer width - // in case reachability has already been computed - sp->reachable = s->reachable; + // addstring, addaddr, etc., mark the symbols as reachable. + // In this case that is not necessarily true, so stick to what + // we know before entering this function. + s->reachable = reachable; + sp->reachable = reachable; } vlong diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 3edf7253d4..f889aba8a9 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -222,8 +222,10 @@ loadlib(void) // Since we are simulating the import, we have to provide this string. cgostrsym = "go.string.\"runtime/cgo\""; if(linkrlookup(ctxt, cgostrsym, 0) == nil) { + s = linklookup(ctxt, cgostrsym, 0); + s->type = SRODATA; + s->reachable = 1; addstrdata(cgostrsym, "runtime/cgo"); - linklookup(ctxt, cgostrsym, 0)->type = SRODATA; } } diff --git a/test/linkx.go b/test/linkx.go index 36d16aec9b..06888a229a 100644 --- a/test/linkx.go +++ b/test/linkx.go @@ -1,4 +1,4 @@ -// $G $D/$F.go && $L -X main.tbd hello -X main.overwrite trumped $F.$A && ./$A.out +// $G $D/$F.go && $L -X main.tbd hello -X main.overwrite trumped -X main.nosuchsymbol neverseen $F.$A && ./$A.out // NOTE: This test is not run by 'run.go' and so not run by all.bash. // To run this test you must use the ./run shell script. -- cgit v1.3 From e1364a6d0ecd5ba50845f416bb3c016bc54a3648 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Sun, 28 Sep 2014 23:52:08 -0700 Subject: runtime: fix cgo_topofstack to save clobbered registers Fixes #8816 At least, I hope it does. TBR=rsc CC=golang-codereviews https://golang.org/cl/153730043 --- src/runtime/asm_arm.s | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s index 06bd0751db..36fb022f95 100644 --- a/src/runtime/asm_arm.s +++ b/src/runtime/asm_arm.s @@ -1303,9 +1303,17 @@ yieldloop: // Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Must obey the gcc calling convention. -TEXT _cgo_topofstack(SB),NOSPLIT,$0 +TEXT _cgo_topofstack(SB),NOSPLIT,$8 + // R11 and g register are clobbered by load_g. They are + // callee-save in the gcc calling convention, so save them here. + MOVW R11, saveR11-4(SP) + MOVW g, saveG-8(SP) + BL runtime·load_g(SB) MOVW g_m(g), R0 MOVW m_curg(R0), R0 MOVW (g_stack+stack_hi)(R0), R0 + + MOVW saveG-8(SP), g + MOVW saveR11-4(SP), R11 RET -- cgit v1.3 From e7e3b3ec1037669c90851670e2dc608b80d499d6 Mon Sep 17 00:00:00 2001 From: Jonathan Rudenberg Date: Mon, 29 Sep 2014 12:13:22 -0700 Subject: cmd/ld: close outfile before cleanup This prevents the temporary directory from being leaked when the linker is run on a FUSE filesystem. Fixes #8684. LGTM=bradfitz R=golang-codereviews, rsc, bradfitz CC=golang-codereviews https://golang.org/cl/141840043 --- src/cmd/ld/lib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index f889aba8a9..910201bdbb 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -531,8 +531,9 @@ char* mktempdir(void); void removeall(char*); static void -rmtemp(void) +cleanup(void) { + close(cout); removeall(tmpdir); } @@ -547,7 +548,7 @@ hostlinksetup(void) // create temporary directory and arrange cleanup if(tmpdir == nil) { tmpdir = mktempdir(); - atexit(rmtemp); + atexit(cleanup); } // change our output to temporary object file -- cgit v1.3 From bd72d2c650d0b7b668a8b3de58e60b8b61278956 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Mon, 29 Sep 2014 12:23:43 -0700 Subject: go/build: add go1.4 tag. LGTM=bradfitz R=bradfitz CC=golang-codereviews https://golang.org/cl/138000044 --- src/go/build/build.go | 6 +++--- src/go/build/doc.go | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/go/build/build.go b/src/go/build/build.go index 69cb4b2f6b..5e11c9b9c5 100644 --- a/src/go/build/build.go +++ b/src/go/build/build.go @@ -294,10 +294,10 @@ func defaultContext() Context { // say "+build go1.x", and code that should only be built before Go 1.x // (perhaps it is the stub to use in that case) should say "+build !go1.x". // - // When we reach Go 1.4 the line will read - // c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4"} + // When we reach Go 1.5 the line will read + // c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5"} // and so on. - c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3"} + c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4"} switch os.Getenv("CGO_ENABLED") { case "1": diff --git a/src/go/build/doc.go b/src/go/build/doc.go index d78ef3f1c8..56878f2b4a 100644 --- a/src/go/build/doc.go +++ b/src/go/build/doc.go @@ -100,6 +100,7 @@ // - "go1.1", from Go version 1.1 onward // - "go1.2", from Go version 1.2 onward // - "go1.3", from Go version 1.3 onward +// - "go1.4", from Go version 1.4 onward // - any additional words listed in ctxt.BuildTags // // If a file's name, after stripping the extension and a possible _test suffix, -- cgit v1.3 From dca460574f28bee2c096eaafb82cbc4f88069c0b Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 29 Sep 2014 12:24:06 -0700 Subject: net: fix misleading package comment example Fixes #8607 LGTM=r R=r CC=golang-codereviews https://golang.org/cl/146470043 --- src/net/net.go | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/net/net.go b/src/net/net.go index ca56af54fc..cb31af5e34 100644 --- a/src/net/net.go +++ b/src/net/net.go @@ -32,7 +32,6 @@ The Listen function creates servers: conn, err := ln.Accept() if err != nil { // handle error - continue } go handleConnection(conn) } -- cgit v1.3 From dfddd802ace3aece85985dcd4b16e2488f287477 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Mon, 29 Sep 2014 12:26:51 -0700 Subject: crypto/x509: accept CRLs without an expiry. RFC5280 says that the nextUpdate field is optional. Fixes #8085. R=bradfitz CC=golang-codereviews https://golang.org/cl/149770044 --- src/crypto/x509/pkix/pkix.go | 2 +- src/crypto/x509/x509_test.go | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/crypto/x509/pkix/pkix.go b/src/crypto/x509/pkix/pkix.go index 58c1e54d10..8768b78590 100644 --- a/src/crypto/x509/pkix/pkix.go +++ b/src/crypto/x509/pkix/pkix.go @@ -164,7 +164,7 @@ type TBSCertificateList struct { Signature AlgorithmIdentifier Issuer RDNSequence ThisUpdate time.Time - NextUpdate time.Time + NextUpdate time.Time `asn1:"optional"` RevokedCertificates []RevokedCertificate `asn1:"optional"` Extensions []Extension `asn1:"tag:0,optional,explicit"` } diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go index 56f7a98322..abe86216f9 100644 --- a/src/crypto/x509/x509_test.go +++ b/src/crypto/x509/x509_test.go @@ -707,6 +707,17 @@ func TestParseDERCRL(t *testing.T) { // Can't check the signature here without a package cycle. } +func TestCRLWithoutExpiry(t *testing.T) { + derBytes := fromBase64("MIHYMIGZMAkGByqGSM44BAMwEjEQMA4GA1UEAxMHQ2FybERTUxcNOTkwODI3MDcwMDAwWjBpMBMCAgDIFw05OTA4MjIwNzAwMDBaMBMCAgDJFw05OTA4MjIwNzAwMDBaMBMCAgDTFw05OTA4MjIwNzAwMDBaMBMCAgDSFw05OTA4MjIwNzAwMDBaMBMCAgDUFw05OTA4MjQwNzAwMDBaMAkGByqGSM44BAMDLwAwLAIUfmVSdjP+NHMX0feW+aDU2G1cfT0CFAJ6W7fVWxjBz4fvftok8yqDnDWh") + certList, err := ParseDERCRL(derBytes) + if err != nil { + t.Fatal(err) + } + if !certList.TBSCertList.NextUpdate.IsZero() { + t.Errorf("NextUpdate is not the zero value") + } +} + func TestParsePEMCRL(t *testing.T) { pemBytes := fromBase64(pemCRLBase64) certList, err := ParseCRL(pemBytes) -- cgit v1.3 From 1cfa5958f0ab841bd00fcc0d674bfec87e2055bd Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 29 Sep 2014 13:28:08 -0700 Subject: undo CL 141840043 / 65e21380cb2a MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unnecessary; covered by https://golang.org/cl/141690043 Verified by jonathan@titanous.com on golang-dev. ««« original CL description cmd/ld: close outfile before cleanup This prevents the temporary directory from being leaked when the linker is run on a FUSE filesystem. Fixes #8684. LGTM=bradfitz R=golang-codereviews, rsc, bradfitz CC=golang-codereviews https://golang.org/cl/141840043 »»» LGTM=jonathan, iant R=iant, jonathan CC=golang-codereviews https://golang.org/cl/150250045 --- src/cmd/ld/lib.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 910201bdbb..f889aba8a9 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -531,9 +531,8 @@ char* mktempdir(void); void removeall(char*); static void -cleanup(void) +rmtemp(void) { - close(cout); removeall(tmpdir); } @@ -548,7 +547,7 @@ hostlinksetup(void) // create temporary directory and arrange cleanup if(tmpdir == nil) { tmpdir = mktempdir(); - atexit(cleanup); + atexit(rmtemp); } // change our output to temporary object file -- cgit v1.3 From fe2bc11e1fac672cc23e9ffd01673257a1330707 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 29 Sep 2014 13:32:14 -0700 Subject: cmd/yacc: fix handling of tokens that don't start with letters CL 149110043 changed yacc to no longer keep a leading space for quoted tokens. That is OK by itself but unfortunately yacc was relying on that leading space to notice which tokens it should not output as const declarations. Add a few such tokens to expr.y, although it won't make any immediate difference as we seem to have no tests for yacc. LGTM=rsc R=golang-codereviews, rsc CC=golang-codereviews https://golang.org/cl/152720043 --- src/cmd/yacc/testdata/expr/expr.y | 2 ++ src/cmd/yacc/yacc.go | 14 +++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/cmd/yacc/testdata/expr/expr.y b/src/cmd/yacc/testdata/expr/expr.y index 09451949ff..721b1c9172 100644 --- a/src/cmd/yacc/testdata/expr/expr.y +++ b/src/cmd/yacc/testdata/expr/expr.y @@ -32,6 +32,8 @@ import ( %type expr expr1 expr2 expr3 +%token '+' '-' '*' '/' '(' ')' + %token NUM %% diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go index 0761811cf4..4dba376fc2 100644 --- a/src/cmd/yacc/yacc.go +++ b/src/cmd/yacc/yacc.go @@ -195,8 +195,9 @@ type Item struct { } type Symb struct { - name string - value int + name string + noconst bool + value int } type Wset struct { @@ -509,8 +510,7 @@ outer: // put out non-literal terminals for i := TOKSTART; i <= ntokens; i++ { // non-literals - c := tokset[i].name[0] - if c != ' ' && c != '$' { + if !tokset[i].noconst { fmt.Fprintf(ftable, "const %v = %v\n", tokset[i].name, tokset[i].value) } } @@ -734,7 +734,7 @@ func defin(nt int, s string) int { copy(anontrst, nontrst) nontrst = anontrst } - nontrst[nnonter] = Symb{s, 0} + nontrst[nnonter] = Symb{name: s} return NTBASE + nnonter } @@ -769,9 +769,13 @@ func defin(nt int, s string) int { if val == 0 { errorf("token value 0 is illegal") } + tokset[ntokens].noconst = true } else { val = extval extval++ + if s[0] == '$' { + tokset[ntokens].noconst = true + } } tokset[ntokens].value = val -- cgit v1.3 From 705c1f5cd45d572ba32dea48f5fe997a9f970400 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 29 Sep 2014 13:42:33 -0700 Subject: net/http: clarify Request.FormValue docs Fixes #8067 LGTM=r R=r CC=golang-codereviews https://golang.org/cl/146480043 --- src/net/http/request.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/net/http/request.go b/src/net/http/request.go index 263c26c9bd..487eebcb84 100644 --- a/src/net/http/request.go +++ b/src/net/http/request.go @@ -852,7 +852,8 @@ func (r *Request) ParseMultipartForm(maxMemory int64) error { // POST and PUT body parameters take precedence over URL query string values. // FormValue calls ParseMultipartForm and ParseForm if necessary and ignores // any errors returned by these functions. -// To access multiple values of the same key use ParseForm. +// To access multiple values of the same key, call ParseForm and +// then inspect Request.Form directly. func (r *Request) FormValue(key string) string { if r.Form == nil { r.ParseMultipartForm(defaultMaxMemory) -- cgit v1.3 From 2da734189db9a7ad7d6de259ebe9003d20f9f291 Mon Sep 17 00:00:00 2001 From: James Tucker Date: Mon, 29 Sep 2014 13:53:42 -0700 Subject: net/http: enable Transfer-Encoding: identity without Content-Length for HTTP 1.1. Use case is SSE recommended configuration: http://www.w3.org/TR/eventsource/#notes Removes a TODO. LGTM=bradfitz R=golang-codereviews, bradfitz, tommi.virtanen CC=golang-codereviews https://golang.org/cl/100000044 --- src/net/http/serve_test.go | 29 +++++++++++++++++++++++++++++ src/net/http/server.go | 21 ++++++++++++++------- 2 files changed, 43 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go index a690ae4699..702bffdc13 100644 --- a/src/net/http/serve_test.go +++ b/src/net/http/serve_test.go @@ -778,6 +778,35 @@ func TestChunkedResponseHeaders(t *testing.T) { } } +func TestIdentityResponseHeaders(t *testing.T) { + defer afterTest(t) + log.SetOutput(ioutil.Discard) // is noisy otherwise + defer log.SetOutput(os.Stderr) + + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("Transfer-Encoding", "identity") + w.(Flusher).Flush() + fmt.Fprintf(w, "I am an identity response.") + })) + defer ts.Close() + + res, err := Get(ts.URL) + if err != nil { + t.Fatalf("Get error: %v", err) + } + defer res.Body.Close() + + if g, e := res.TransferEncoding, []string(nil); !reflect.DeepEqual(g, e) { + t.Errorf("expected TransferEncoding of %v; got %v", e, g) + } + if _, haveCL := res.Header["Content-Length"]; haveCL { + t.Errorf("Unexpected Content-Length") + } + if !res.Close { + t.Errorf("expected Connection: close; got %v", res.Close) + } +} + // Test304Responses verifies that 304s don't declare that they're // chunking in their response headers and aren't allowed to produce // output. diff --git a/src/net/http/server.go b/src/net/http/server.go index 7ad0bcbc20..b5959f7321 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -839,13 +839,20 @@ func (cw *chunkWriter) writeHeader(p []byte) { } else if hasCL { delHeader("Transfer-Encoding") } else if w.req.ProtoAtLeast(1, 1) { - // HTTP/1.1 or greater: use chunked transfer encoding - // to avoid closing the connection at EOF. - // TODO: this blows away any custom or stacked Transfer-Encoding they - // might have set. Deal with that as need arises once we have a valid - // use case. - cw.chunking = true - setHeader.transferEncoding = "chunked" + // HTTP/1.1 or greater: Transfer-Encoding has been set to identity, and no + // content-length has been provided. The connection must be closed after the + // reply is written, and no chunking is to be done. This is the setup + // recommended in the Server-Sent Events candidate recommendation 11, + // section 8. + if hasTE && te == "identity" { + cw.chunking = false + w.closeAfterReply = true + } else { + // HTTP/1.1 or greater: use chunked transfer encoding + // to avoid closing the connection at EOF. + cw.chunking = true + setHeader.transferEncoding = "chunked" + } } else { // HTTP version < 1.1: cannot do chunked transfer // encoding and we don't know the Content-Length so -- cgit v1.3 From b4380a3ba2da2515225ab45ff07082d67399757a Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Mon, 29 Sep 2014 14:05:33 -0700 Subject: runtime: delete unused variables. We're not comparing with code addresses any more. Instead, we use nil algorithm functions to mark uncomparable types. LGTM=bradfitz R=golang-codereviews, bradfitz CC=golang-codereviews https://golang.org/cl/151040044 --- src/runtime/stubs.go | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src') diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go index 1381c7efdb..c6a9cf9f54 100644 --- a/src/runtime/stubs.go +++ b/src/runtime/stubs.go @@ -148,10 +148,6 @@ func fastrand1() uint32 //go:noescape func memeq(a, b unsafe.Pointer, size uintptr) bool -// Code pointers for the nohash/noequal algorithms. Used for producing better error messages. -var nohashcode uintptr -var noequalcode uintptr - // noescape hides a pointer from escape analysis. noescape is // the identity function but escape analysis doesn't think the // output depends on the input. noescape is inlined and currently -- cgit v1.3 From 5368e63b57f742495fcbbb82bb15772b761004bf Mon Sep 17 00:00:00 2001 From: Tom Linford Date: Tue, 30 Sep 2014 09:51:49 +1000 Subject: x509: add root certs for android. On android, root certificates appear to be stored in the folder /system/etc/security/cacerts, which has many certs in several different files. This change adds a new array of directories in which certs can be found. To test this, I simply tried making a request with the http library to an HTTPS URL on an android emulator and manually verified that it worked. LGTM=crawshaw R=golang-codereviews, gobot, crawshaw CC=golang-codereviews https://golang.org/cl/151800043 --- src/crypto/x509/root_unix.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'src') diff --git a/src/crypto/x509/root_unix.go b/src/crypto/x509/root_unix.go index 11ad3c440d..10057c0c03 100644 --- a/src/crypto/x509/root_unix.go +++ b/src/crypto/x509/root_unix.go @@ -17,6 +17,13 @@ var certFiles = []string{ "/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly } +// Possible directories with certificate files; stop after successfully +// reading at least one file from a directory. +var certDirectories = []string{ + "/system/etc/security/cacerts", // Android + +} + func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { return nil, nil } @@ -32,6 +39,24 @@ func initSystemRoots() { } } + for _, directory := range certDirectories { + fis, err := ioutil.ReadDir(directory) + if err != nil { + continue + } + rootsAdded := false + for _, fi := range fis { + data, err := ioutil.ReadFile(directory + "/" + fi.Name()) + if err == nil && roots.AppendCertsFromPEM(data) { + rootsAdded = true + } + } + if rootsAdded { + systemRoots = roots + return + } + } + // All of the files failed to load. systemRoots will be nil which will // trigger a specific error at verification time. } -- cgit v1.3 From 0b36211cfb823f41e3a201dd18ddee7a68b4d4e3 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Tue, 30 Sep 2014 10:03:10 +1000 Subject: liblink: generate MRC replacement in liblink, not tls_arm Fixes #8690. This CL moves the save of LR around BL runtime.read_tls_fallback to liblink as it is not needed when MRC is not replaced. LGTM=rsc, minux R=rsc, khr, minux CC=golang-codereviews https://golang.org/cl/147310043 --- src/liblink/obj5.c | 20 ++++++++++++++++++-- src/runtime/tls_arm.s | 11 +++-------- 2 files changed, 21 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/liblink/obj5.c b/src/liblink/obj5.c index e192b082b5..d7f2714ed8 100644 --- a/src/liblink/obj5.c +++ b/src/liblink/obj5.c @@ -119,14 +119,30 @@ progedit(Link *ctxt, Prog *p) ctxt->diag("%L: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p->lineno); if(ctxt->goarm < 7) { - // Replace it with BL runtime.read_tls_fallback(SB). + // Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension. if(tlsfallback == nil) tlsfallback = linklookup(ctxt, "runtime.read_tls_fallback", 0); - // BL runtime.read_tls_fallback(SB) + // MOVW LR, R11 + p->as = AMOVW; + p->from.type = D_REG; + p->from.reg = REGLINK; + p->to.type = D_REG; + p->to.reg = REGTMP; + + // BL runtime.read_tls_fallback(SB) + p = appendp(ctxt, p); p->as = ABL; p->to.type = D_BRANCH; p->to.sym = tlsfallback; p->to.offset = 0; + + // MOVW R11, LR + p = appendp(ctxt, p); + p->as = AMOVW; + p->from.type = D_REG; + p->from.reg = REGTMP; + p->to.type = D_REG; + p->to.reg = REGLINK; break; } } diff --git a/src/runtime/tls_arm.s b/src/runtime/tls_arm.s index 039b013833..85c3940bf2 100644 --- a/src/runtime/tls_arm.s +++ b/src/runtime/tls_arm.s @@ -31,11 +31,8 @@ TEXT runtime·save_g(SB),NOSPLIT,$-4 #endif // If the host does not support MRC the linker will replace it with // a call to runtime.read_tls_fallback which jumps to __kuser_get_tls. - // Both functions are written to only disturb R0 so it should be safe to - // use R11 here to temporarily store LR. - MOVW LR, R11 + // The replacement function saves LR in R11 over the call to read_tls_fallback. MRC 15, 0, R0, C13, C0, 3 // fetch TLS base pointer - MOVW R11, LR // $runtime.tlsg(SB) is a special linker symbol. // It is the offset from the TLS base pointer to our // thread-local storage for g. @@ -57,10 +54,8 @@ TEXT runtime·load_g(SB),NOSPLIT,$0 // nothing to do as nacl/arm does not use TLS at all. RET #endif - // See comment in save_g. - MOVW LR, R11 - MRC 15, 0, R0, C13, C0, 3 // fetch TLS base pointer - MOVW R11, LR + // See save_g + MRC 15, 0, R0, C13, C0, 3 // fetch TLS base pointer // $runtime.tlsg(SB) is a special linker symbol. // It is the offset from the TLS base pointer to our // thread-local storage for g. -- cgit v1.3 From 912ec1990bd09f8fc128c3fa6b59105085aabc03 Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Mon, 29 Sep 2014 17:04:48 -0700 Subject: go/format, cmd/gofmt: fix issues with partial Go code with indent Fixes #5551. Fixes #4449. Adds tests for both issues. Note that the two issues occur only when formatting partial Go code with indent. The best way to understand the change is as follows: I took the code of cmd/gofmt and go/format, combined it into one unified code that does not suffer from either 4449 nor 5551, and then applied that code to both cmd/gofmt and go/format. As a result, there is now much more identical code between the two packages, making future code deduplication easier (it was not possible to do that now without adding public APIs, which I was advised not to do at this time). More specifically, I took the parse() of cmd/gofmt which correctly preserves comments (issue 5551) and modified it to fix issue where it would sometimes modify literal values (issue 4449). I ended up removing the matchSpace() function because it no longer needed to do some of its work (insert indent), and a part of its work had to be done in advance (determining the indentation of first code line), because that calculation is required for cfg.Fprint() to run. adjustIndent is used to adjust the indent of cfg.Fprint() to compensate for the body of wrapper func being indented by one level. This allows to get rid of the bytes.Replace text manipulation of inner content, which was problematic and sometimes altered raw string literals (issue 4449). This means that sometimes the value of cfg.Indent is negative, but that works as expected. So now the algorithm for formatting partial Go code is: 1. Determine and prepend leading space of original source. 2. Determine and prepend indentation of first code line. 3. Format and write partial Go code (with all of its leading & trailing space trimmed). 4. Determine and append trailing space of original source. LGTM=gri R=golang-codereviews, bradfitz, gri CC=golang-codereviews https://golang.org/cl/142360043 --- src/cmd/gofmt/gofmt.go | 163 +++++++++++++++------------ src/cmd/gofmt/long_test.go | 4 +- src/cmd/gofmt/testdata/stdin6.golden | 19 ++++ src/cmd/gofmt/testdata/stdin6.input | 21 ++++ src/cmd/gofmt/testdata/stdin7.golden | 19 ++++ src/cmd/gofmt/testdata/stdin7.input | 21 ++++ src/go/format/format.go | 207 +++++++++++++++++++++-------------- src/go/format/format_test.go | 6 +- 8 files changed, 305 insertions(+), 155 deletions(-) create mode 100644 src/cmd/gofmt/testdata/stdin6.golden create mode 100644 src/cmd/gofmt/testdata/stdin6.input create mode 100644 src/cmd/gofmt/testdata/stdin7.golden create mode 100644 src/cmd/gofmt/testdata/stdin7.input (limited to 'src') diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index f322a2b0a0..8542957248 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -87,13 +87,13 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error return err } - file, adjust, err := parse(fileSet, filename, src, stdin) + file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, stdin) if err != nil { return err } if rewrite != nil { - if adjust == nil { + if sourceAdj == nil { file = rewrite(file) } else { fmt.Fprintf(os.Stderr, "warning: rewrite ignored for incomplete programs\n") @@ -106,15 +106,10 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error simplify(file) } - var buf bytes.Buffer - err = (&printer.Config{Mode: printerMode, Tabwidth: tabWidth}).Fprint(&buf, fileSet, file) + res, err := format(fileSet, file, sourceAdj, indentAdj, src) if err != nil { return err } - res := buf.Bytes() - if adjust != nil { - res = adjust(src, res) - } if !bytes.Equal(src, res) { // formatting has changed @@ -242,17 +237,19 @@ func diff(b1, b2 []byte) (data []byte, err error) { // parse parses src, which was read from filename, // as a Go source file or statement list. -func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.File, func(orig, src []byte) []byte, error) { +func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) ( + file *ast.File, + sourceAdj func(src []byte, indent int) []byte, + indentAdj int, + err error, +) { // Try as whole source file. - file, err := parser.ParseFile(fset, filename, src, parserMode) - if err == nil { - return file, nil, nil - } - // If the error is that the source file didn't begin with a - // package line and this is standard input, fall through to + file, err = parser.ParseFile(fset, filename, src, parserMode) + // If there's no error, return. If the error is that the source file didn't begin with a + // package line and source fragments are ok, fall through to // try as a source fragment. Stop and return on any other error. - if !stdin || !strings.Contains(err.Error(), "expected 'package'") { - return nil, nil, err + if err == nil || !fragmentOk || !strings.Contains(err.Error(), "expected 'package'") { + return } // If this is a declaration list, make it a source file @@ -262,19 +259,19 @@ func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.F psrc := append([]byte("package p;"), src...) file, err = parser.ParseFile(fset, filename, psrc, parserMode) if err == nil { - adjust := func(orig, src []byte) []byte { + sourceAdj = func(src []byte, indent int) []byte { // Remove the package clause. // Gofmt has turned the ; into a \n. - src = src[len("package p\n"):] - return matchSpace(orig, src) + src = src[indent+len("package p\n"):] + return bytes.TrimSpace(src) } - return file, adjust, nil + return } // If the error is that the source file didn't begin with a // declaration, fall through to try as a statement list. // Stop and return on any other error. if !strings.Contains(err.Error(), "expected declaration") { - return nil, nil, err + return } // If this is a statement list, make it a source file @@ -285,65 +282,89 @@ func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.F fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '}') file, err = parser.ParseFile(fset, filename, fsrc, parserMode) if err == nil { - adjust := func(orig, src []byte) []byte { + sourceAdj = func(src []byte, indent int) []byte { + // Cap adjusted indent to zero. + if indent < 0 { + indent = 0 + } // Remove the wrapping. // Gofmt has turned the ; into a \n\n. - src = src[len("package p\n\nfunc _() {"):] - src = src[:len(src)-len("\n}\n")] - // Gofmt has also indented the function body one level. - // Remove that indent. - src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1) - return matchSpace(orig, src) + // There will be two non-blank lines with indent, hence 2*indent. + src = src[2*indent+len("package p\n\nfunc _() {"):] + src = src[:len(src)-(indent+len("\n}\n"))] + return bytes.TrimSpace(src) } - return file, adjust, nil + // Gofmt has also indented the function body one level. + // Adjust that with indentAdj. + indentAdj = -1 } - // Failed, and out of options. - return nil, nil, err + // Succeeded, or out of options. + return } -func cutSpace(b []byte) (before, middle, after []byte) { - i := 0 - for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') { - i++ - } - j := len(b) - for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') { - j-- - } - if i <= j { - return b[:i], b[i:j], b[j:] +func format(fset *token.FileSet, file *ast.File, sourceAdj func(src []byte, indent int) []byte, indentAdj int, src []byte) ([]byte, error) { + if sourceAdj == nil { + // Complete source file. + var buf bytes.Buffer + err := (&printer.Config{Mode: printerMode, Tabwidth: tabWidth}).Fprint(&buf, fset, file) + if err != nil { + return nil, err + } + return buf.Bytes(), nil } - return nil, nil, b[j:] -} -// matchSpace reformats src to use the same space context as orig. -// 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src. -// 2) matchSpace copies the indentation of the first non-blank line in orig -// to every non-blank line in src. -// 3) matchSpace copies the trailing space from orig and uses it in place -// of src's trailing space. -func matchSpace(orig []byte, src []byte) []byte { - before, _, after := cutSpace(orig) - i := bytes.LastIndex(before, []byte{'\n'}) - before, indent := before[:i+1], before[i+1:] - - _, src, _ = cutSpace(src) - - var b bytes.Buffer - b.Write(before) - for len(src) > 0 { - line := src - if i := bytes.IndexByte(line, '\n'); i >= 0 { - line, src = line[:i+1], line[i+1:] - } else { - src = nil + // Partial source file. + // Determine and prepend leading space. + i, j := 0, 0 + for j < len(src) && isSpace(src[j]) { + if src[j] == '\n' { + i = j + 1 // byte offset of last line in leading space } - if len(line) > 0 && line[0] != '\n' { // not blank - b.Write(indent) + j++ + } + var res []byte + res = append(res, src[:i]...) + + // Determine and prepend indentation of first code line. + // Spaces are ignored unless there are no tabs, + // in which case spaces count as one tab. + indent := 0 + hasSpace := false + for _, b := range src[i:j] { + switch b { + case ' ': + hasSpace = true + case '\t': + indent++ } - b.Write(line) } - b.Write(after) - return b.Bytes() + if indent == 0 && hasSpace { + indent = 1 + } + for i := 0; i < indent; i++ { + res = append(res, '\t') + } + + // Format the source. + // Write it without any leading and trailing space. + cfg := &printer.Config{Mode: printerMode, Tabwidth: tabWidth} + cfg.Indent = indent + indentAdj + var buf bytes.Buffer + err := cfg.Fprint(&buf, fset, file) + if err != nil { + return nil, err + } + res = append(res, sourceAdj(buf.Bytes(), cfg.Indent)...) + + // Determine and append trailing space. + i = len(src) + for i > 0 && isSpace(src[i-1]) { + i-- + } + return append(res, src[i:]...), nil +} + +func isSpace(b byte) bool { + return b == ' ' || b == '\t' || b == '\n' || b == '\r' } diff --git a/src/cmd/gofmt/long_test.go b/src/cmd/gofmt/long_test.go index 108278b336..237b86021b 100644 --- a/src/cmd/gofmt/long_test.go +++ b/src/cmd/gofmt/long_test.go @@ -32,7 +32,7 @@ var ( ) func gofmt(fset *token.FileSet, filename string, src *bytes.Buffer) error { - f, _, err := parse(fset, filename, src.Bytes(), false) + f, _, _, err := parse(fset, filename, src.Bytes(), false) if err != nil { return err } @@ -60,7 +60,7 @@ func testFile(t *testing.T, b1, b2 *bytes.Buffer, filename string) { // exclude files w/ syntax errors (typically test cases) fset := token.NewFileSet() - if _, _, err = parse(fset, filename, b1.Bytes(), false); err != nil { + if _, _, _, err = parse(fset, filename, b1.Bytes(), false); err != nil { if *verbose { fmt.Fprintf(os.Stderr, "ignoring %s\n", err) } diff --git a/src/cmd/gofmt/testdata/stdin6.golden b/src/cmd/gofmt/testdata/stdin6.golden new file mode 100644 index 0000000000..ffcea8011b --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin6.golden @@ -0,0 +1,19 @@ + //gofmt -stdin + + if err != nil { + source := strings.NewReader(`line 1. +line 2. +`) + return source + } + + f := func(hat, tail string) { + + fmt.Println(hat+` +foo + + +`+tail, + "more", + "and more") + } diff --git a/src/cmd/gofmt/testdata/stdin6.input b/src/cmd/gofmt/testdata/stdin6.input new file mode 100644 index 0000000000..78330020c6 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin6.input @@ -0,0 +1,21 @@ + //gofmt -stdin + + if err != nil { + source := strings.NewReader(`line 1. +line 2. +`) + return source + } + + f:=func( hat, tail string){ + + + + fmt. Println ( hat+ ` +foo + + +`+ tail , + "more" , + "and more" ) + } diff --git a/src/cmd/gofmt/testdata/stdin7.golden b/src/cmd/gofmt/testdata/stdin7.golden new file mode 100644 index 0000000000..bbac7133c8 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin7.golden @@ -0,0 +1,19 @@ + //gofmt -stdin + + if err != nil { + source := strings.NewReader(`line 1. +line 2. +`) + return source + } + + f := func(hat, tail string) { + + fmt.Println(hat+` + foo + + + `+tail, + "more", + "and more") + } diff --git a/src/cmd/gofmt/testdata/stdin7.input b/src/cmd/gofmt/testdata/stdin7.input new file mode 100644 index 0000000000..fd772a3c4e --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin7.input @@ -0,0 +1,21 @@ + //gofmt -stdin + + if err != nil { + source := strings.NewReader(`line 1. +line 2. +`) + return source + } + + f:=func( hat, tail string){ + + + + fmt. Println ( hat+ ` + foo + + + `+ tail , + "more" , + "and more" ) + } diff --git a/src/go/format/format.go b/src/go/format/format.go index 3d00a645db..08a9047b99 100644 --- a/src/go/format/format.go +++ b/src/go/format/format.go @@ -18,6 +18,8 @@ import ( var config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} +const parserMode = parser.ParseComments + // Node formats node in canonical gofmt style and writes the result to dst. // // The node type must be *ast.File, *printer.CommentedNode, []ast.Decl, @@ -52,7 +54,7 @@ func Node(dst io.Writer, fset *token.FileSet, node interface{}) error { if err != nil { return err } - file, err = parser.ParseFile(fset, "", buf.Bytes(), parser.ParseComments) + file, err = parser.ParseFile(fset, "", buf.Bytes(), parserMode) if err != nil { // We should never get here. If we do, provide good diagnostic. return fmt.Errorf("format.Node internal error (%s)", err) @@ -80,66 +82,12 @@ func Node(dst io.Writer, fset *token.FileSet, node interface{}) error { // func Source(src []byte) ([]byte, error) { fset := token.NewFileSet() - node, err := parse(fset, src) + file, sourceAdj, indentAdj, err := parse(fset, "", src, true) if err != nil { return nil, err } - var buf bytes.Buffer - if file, ok := node.(*ast.File); ok { - // Complete source file. - ast.SortImports(fset, file) - err := config.Fprint(&buf, fset, file) - if err != nil { - return nil, err - } - - } else { - // Partial source file. - // Determine and prepend leading space. - i, j := 0, 0 - for j < len(src) && isSpace(src[j]) { - if src[j] == '\n' { - i = j + 1 // index of last line in leading space - } - j++ - } - buf.Write(src[:i]) - - // Determine indentation of first code line. - // Spaces are ignored unless there are no tabs, - // in which case spaces count as one tab. - indent := 0 - hasSpace := false - for _, b := range src[i:j] { - switch b { - case ' ': - hasSpace = true - case '\t': - indent++ - } - } - if indent == 0 && hasSpace { - indent = 1 - } - - // Format the source. - cfg := config - cfg.Indent = indent - err := cfg.Fprint(&buf, fset, node) - if err != nil { - return nil, err - } - - // Determine and append trailing space. - i = len(src) - for i > 0 && isSpace(src[i-1]) { - i-- - } - buf.Write(src[i:]) - } - - return buf.Bytes(), nil + return format(fset, file, sourceAdj, indentAdj, src) } func hasUnsortedImports(file *ast.File) bool { @@ -160,40 +108,137 @@ func hasUnsortedImports(file *ast.File) bool { return false } -func isSpace(b byte) bool { - return b == ' ' || b == '\t' || b == '\n' || b == '\r' -} +// parse parses src, which was read from filename, +// as a Go source file or statement list. +func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) ( + file *ast.File, + sourceAdj func(src []byte, indent int) []byte, + indentAdj int, + err error, +) { + // Try as whole source file. + file, err = parser.ParseFile(fset, filename, src, parserMode) + // If there's no error, return. If the error is that the source file didn't begin with a + // package line and source fragments are ok, fall through to + // try as a source fragment. Stop and return on any other error. + if err == nil || !fragmentOk || !strings.Contains(err.Error(), "expected 'package'") { + return + } -func parse(fset *token.FileSet, src []byte) (interface{}, error) { - // Try as a complete source file. - file, err := parser.ParseFile(fset, "", src, parser.ParseComments) + // If this is a declaration list, make it a source file + // by inserting a package clause. + // Insert using a ;, not a newline, so that the line numbers + // in psrc match the ones in src. + psrc := append([]byte("package p;"), src...) + file, err = parser.ParseFile(fset, filename, psrc, parserMode) if err == nil { - return file, nil + sourceAdj = func(src []byte, indent int) []byte { + // Remove the package clause. + // Gofmt has turned the ; into a \n. + src = src[indent+len("package p\n"):] + return bytes.TrimSpace(src) + } + return } - // If the source is missing a package clause, try as a source fragment; otherwise fail. - if !strings.Contains(err.Error(), "expected 'package'") { - return nil, err + // If the error is that the source file didn't begin with a + // declaration, fall through to try as a statement list. + // Stop and return on any other error. + if !strings.Contains(err.Error(), "expected declaration") { + return } - // Try as a declaration list by prepending a package clause in front of src. - // Use ';' not '\n' to keep line numbers intact. - psrc := append([]byte("package p;"), src...) - file, err = parser.ParseFile(fset, "", psrc, parser.ParseComments) + // If this is a statement list, make it a source file + // by inserting a package clause and turning the list + // into a function body. This handles expressions too. + // Insert using a ;, not a newline, so that the line numbers + // in fsrc match the ones in src. + fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '}') + file, err = parser.ParseFile(fset, filename, fsrc, parserMode) if err == nil { - return file.Decls, nil + sourceAdj = func(src []byte, indent int) []byte { + // Cap adjusted indent to zero. + if indent < 0 { + indent = 0 + } + // Remove the wrapping. + // Gofmt has turned the ; into a \n\n. + // There will be two non-blank lines with indent, hence 2*indent. + src = src[2*indent+len("package p\n\nfunc _() {"):] + src = src[:len(src)-(indent+len("\n}\n"))] + return bytes.TrimSpace(src) + } + // Gofmt has also indented the function body one level. + // Adjust that with indentAdj. + indentAdj = -1 } - // If the source is missing a declaration, try as a statement list; otherwise fail. - if !strings.Contains(err.Error(), "expected declaration") { + + // Succeeded, or out of options. + return +} + +func format(fset *token.FileSet, file *ast.File, sourceAdj func(src []byte, indent int) []byte, indentAdj int, src []byte) ([]byte, error) { + if sourceAdj == nil { + // Complete source file. + ast.SortImports(fset, file) + var buf bytes.Buffer + err := config.Fprint(&buf, fset, file) + if err != nil { + return nil, err + } + return buf.Bytes(), nil + } + + // Partial source file. + // Determine and prepend leading space. + i, j := 0, 0 + for j < len(src) && isSpace(src[j]) { + if src[j] == '\n' { + i = j + 1 // byte offset of last line in leading space + } + j++ + } + var res []byte + res = append(res, src[:i]...) + + // Determine and prepend indentation of first code line. + // Spaces are ignored unless there are no tabs, + // in which case spaces count as one tab. + indent := 0 + hasSpace := false + for _, b := range src[i:j] { + switch b { + case ' ': + hasSpace = true + case '\t': + indent++ + } + } + if indent == 0 && hasSpace { + indent = 1 + } + for i := 0; i < indent; i++ { + res = append(res, '\t') + } + + // Format the source. + // Write it without any leading and trailing space. + cfg := config + cfg.Indent = indent + indentAdj + var buf bytes.Buffer + err := cfg.Fprint(&buf, fset, file) + if err != nil { return nil, err } + res = append(res, sourceAdj(buf.Bytes(), cfg.Indent)...) - // Try as statement list by wrapping a function around src. - fsrc := append(append([]byte("package p; func _() {"), src...), '}') - file, err = parser.ParseFile(fset, "", fsrc, parser.ParseComments) - if err == nil { - return file.Decls[0].(*ast.FuncDecl).Body.List, nil + // Determine and append trailing space. + i = len(src) + for i > 0 && isSpace(src[i-1]) { + i-- } + return append(res, src[i:]...), nil +} - // Failed, and out of options. - return nil, err +func isSpace(b byte) bool { + return b == ' ' || b == '\t' || b == '\n' || b == '\r' } diff --git a/src/go/format/format_test.go b/src/go/format/format_test.go index 93f0992477..d7846bec65 100644 --- a/src/go/format/format_test.go +++ b/src/go/format/format_test.go @@ -87,7 +87,11 @@ var tests = []string{ "\tx := 0\n\tgo f()\n\n\n", "\n\t\t\n\n\tx := 0\n\tgo f()\n\n\n", "\n\t\t\n\n\t\t\tx := 0\n\t\t\tgo f()\n\n\n", - "\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\nfoo\n`\n\n\n", // no indentation inside raw strings + "\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\nfoo\n`\n\n\n", // no indentation added inside raw strings + "\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\n\t\tfoo\n`\n\n\n", // no indentation removed inside raw strings + + // comments + "i := 5 /* Comment */", // Issue 5551. // erroneous programs "ERROR1 + 2 +", -- cgit v1.3 From f13cec9f5732dd09c51f90957c3d888aad782c27 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 29 Sep 2014 18:16:15 -0700 Subject: net/http: make Transport.CloseIdleConnections also close pending dials See comment 4 of https://code.google.com/p/go/issues/detail?id=8483#c4: "So if a user creates a http.Client, issues a bunch of requests and then wants to shutdown it and all opened connections; what is she intended to do? The report suggests that just waiting for all pending requests and calling CloseIdleConnections won't do, as there can be new racing connections. Obviously she can't do what you've done in the test, as it uses the unexported function. If this happens periodically, it can lead to serious resource leaks (the transport is also preserved alive). Am I missing something?" This CL tracks the user's intention to close all idle connections (CloseIdleConnections sets it true; and making a new request sets it false). If a pending dial finishes and nobody wants it, before it's retained for a future caller, the "wantIdle" bool is checked and it's closed if the user has called CloseIdleConnections without a later call to make a new request. Fixes #8483 LGTM=adg R=golang-codereviews, dvyukov, adg CC=golang-codereviews, rsc https://golang.org/cl/148970043 --- src/net/http/export_test.go | 20 ++++++++++++++++++++ src/net/http/transport.go | 20 +++++++++++++++----- src/net/http/transport_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/net/http/export_test.go b/src/net/http/export_test.go index e5bc02afa2..a6980b5389 100644 --- a/src/net/http/export_test.go +++ b/src/net/http/export_test.go @@ -57,6 +57,26 @@ func (t *Transport) IdleConnChMapSizeForTesting() int { return len(t.idleConnCh) } +func (t *Transport) IsIdleForTesting() bool { + t.idleMu.Lock() + defer t.idleMu.Unlock() + return t.wantIdle +} + +func (t *Transport) RequestIdleConnChForTesting() { + t.getIdleConnCh(connectMethod{nil, "http", "example.com"}) +} + +func (t *Transport) PutIdleTestConn() bool { + c, _ := net.Pipe() + return t.putIdleConn(&persistConn{ + t: t, + conn: c, // dummy + closech: make(chan struct{}), // so it can be closed + cacheKey: connectMethodKey{"", "http", "example.com"}, + }) +} + func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler { f := func() <-chan time.Time { return ch diff --git a/src/net/http/transport.go b/src/net/http/transport.go index f1a6837527..70e574fc86 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -47,13 +47,16 @@ const DefaultMaxIdleConnsPerHost = 2 // HTTPS, and HTTP proxies (for either HTTP or HTTPS with CONNECT). // Transport can also cache connections for future re-use. type Transport struct { - idleMu sync.Mutex - idleConn map[connectMethodKey][]*persistConn - idleConnCh map[connectMethodKey]chan *persistConn + idleMu sync.Mutex + wantIdle bool // user has requested to close all idle conns + idleConn map[connectMethodKey][]*persistConn + idleConnCh map[connectMethodKey]chan *persistConn + reqMu sync.Mutex reqCanceler map[*Request]func() - altMu sync.RWMutex - altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper + + altMu sync.RWMutex + altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper // Proxy specifies a function to return a proxy for a given // Request. If the function returns a non-nil error, the @@ -262,6 +265,7 @@ func (t *Transport) CloseIdleConnections() { m := t.idleConn t.idleConn = nil t.idleConnCh = nil + t.wantIdle = true t.idleMu.Unlock() for _, conns := range m { for _, pconn := range conns { @@ -385,6 +389,11 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool { delete(t.idleConnCh, key) } } + if t.wantIdle { + t.idleMu.Unlock() + pconn.close() + return false + } if t.idleConn == nil { t.idleConn = make(map[connectMethodKey][]*persistConn) } @@ -413,6 +422,7 @@ func (t *Transport) getIdleConnCh(cm connectMethod) chan *persistConn { key := cm.key() t.idleMu.Lock() defer t.idleMu.Unlock() + t.wantIdle = false if t.idleConnCh == nil { t.idleConnCh = make(map[connectMethodKey]chan *persistConn) } diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 2ffd359794..66fcc3c7d4 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -2177,6 +2177,45 @@ func TestRoundTripReturnsProxyError(t *testing.T) { } } +// tests that putting an idle conn after a call to CloseIdleConns does return it +func TestTransportCloseIdleConnsThenReturn(t *testing.T) { + tr := &Transport{} + wantIdle := func(when string, n int) bool { + got := tr.IdleConnCountForTesting("|http|example.com") // key used by PutIdleTestConn + if got == n { + return true + } + t.Errorf("%s: idle conns = %d; want %d", when, got, n) + return false + } + wantIdle("start", 0) + if !tr.PutIdleTestConn() { + t.Fatal("put failed") + } + if !tr.PutIdleTestConn() { + t.Fatal("second put failed") + } + wantIdle("after put", 2) + tr.CloseIdleConnections() + if !tr.IsIdleForTesting() { + t.Error("should be idle after CloseIdleConnections") + } + wantIdle("after close idle", 0) + if tr.PutIdleTestConn() { + t.Fatal("put didn't fail") + } + wantIdle("after second put", 0) + + tr.RequestIdleConnChForTesting() // should toggle the transport out of idle mode + if tr.IsIdleForTesting() { + t.Error("shouldn't be idle after RequestIdleConnChForTesting") + } + if !tr.PutIdleTestConn() { + t.Fatal("after re-activation") + } + wantIdle("after final put", 1) +} + func wantBody(res *http.Response, err error, want string) error { if err != nil { return err -- cgit v1.3 From 70b2da98ca097598326d5d01406b287bcd5eb6ee Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Mon, 29 Sep 2014 21:21:36 -0700 Subject: runtime: initialize traceback variables earlier Our traceback code needs to know the PC of several special functions, including goexit, mcall, etc. Make sure that these PCs are initialized before any traceback occurs. Fixes #8766 LGTM=rsc R=golang-codereviews, rsc, khr, bradfitz CC=golang-codereviews https://golang.org/cl/145570043 --- src/runtime/proc.c | 1 + src/runtime/runtime.h | 1 + src/runtime/traceback.go | 39 +++++++++++++++++++++++++++++---------- 3 files changed, 31 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/runtime/proc.c b/src/runtime/proc.c index 1f0a79098b..e84dc1d048 100644 --- a/src/runtime/proc.c +++ b/src/runtime/proc.c @@ -131,6 +131,7 @@ runtime·schedinit(void) runtime·sched.maxmcount = 10000; + runtime·tracebackinit(); runtime·symtabinit(); runtime·stackinit(); runtime·mallocinit(); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 3a6d3e3262..aa300d7bb8 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -841,6 +841,7 @@ void runtime·mpreinit(M*); void runtime·minit(void); void runtime·unminit(void); void runtime·signalstack(byte*, int32); +void runtime·tracebackinit(void); void runtime·symtabinit(void); Func* runtime·findfunc(uintptr); int32 runtime·funcline(Func*, uintptr, String*); diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index a93c42186b..24dc3eea95 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -31,20 +31,36 @@ import "unsafe" const usesLR = GOARCH != "amd64" && GOARCH != "amd64p32" && GOARCH != "386" var ( - deferprocPC = funcPC(deferproc) - goexitPC = funcPC(goexit) - jmpdeferPC = funcPC(jmpdefer) - mcallPC = funcPC(mcall) - morestackPC = funcPC(morestack) - mstartPC = funcPC(mstart) - newprocPC = funcPC(newproc) - newstackPC = funcPC(newstack) - rt0_goPC = funcPC(rt0_go) - sigpanicPC = funcPC(sigpanic) + // initialized in tracebackinit + deferprocPC uintptr + goexitPC uintptr + jmpdeferPC uintptr + mcallPC uintptr + morestackPC uintptr + mstartPC uintptr + newprocPC uintptr + rt0_goPC uintptr + sigpanicPC uintptr externalthreadhandlerp uintptr // initialized elsewhere ) +func tracebackinit() { + // Go variable initialization happens late during runtime startup. + // Instead of initializing the variables above in the declarations, + // schedinit calls this function so that the variables are + // initialized and available earlier in the startup sequence. + deferprocPC = funcPC(deferproc) + goexitPC = funcPC(goexit) + jmpdeferPC = funcPC(jmpdefer) + mcallPC = funcPC(mcall) + morestackPC = funcPC(morestack) + mstartPC = funcPC(mstart) + newprocPC = funcPC(newproc) + rt0_goPC = funcPC(rt0_go) + sigpanicPC = funcPC(sigpanic) +} + // Traceback over the deferred function calls. // Report them like calls that have been invoked but not started executing yet. func tracebackdefers(gp *g, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer) { @@ -81,6 +97,9 @@ func tracebackdefers(gp *g, callback func(*stkframe, unsafe.Pointer) bool, v uns // collector (callback != nil). A little clunky to merge these, but avoids // duplicating the code and all its subtlety. func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max int, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer, printall bool) int { + if goexitPC == 0 { + gothrow("gentraceback before goexitPC initialization") + } g := getg() gotraceback := gotraceback(nil) if pc0 == ^uintptr(0) && sp0 == ^uintptr(0) { // Signal to fetch saved values from gp. -- cgit v1.3 From 12308d5a0bb424ef3ee9a664c77192b48e3df84c Mon Sep 17 00:00:00 2001 From: Dmitriy Vyukov Date: Tue, 30 Sep 2014 19:34:33 +0400 Subject: runtime: fix throwsplit check Newstack runs on g0, g0->throwsplit is never set. LGTM=rsc R=rsc CC=golang-codereviews, khr https://golang.org/cl/147370043 --- src/runtime/stack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/runtime/stack.c b/src/runtime/stack.c index 2d23c717bd..8562b94076 100644 --- a/src/runtime/stack.c +++ b/src/runtime/stack.c @@ -695,7 +695,7 @@ runtime·newstack(void) runtime·traceback(morebuf.pc, morebuf.sp, morebuf.lr, morebuf.g); runtime·throw("runtime: wrong goroutine in newstack"); } - if(g->throwsplit) + if(g->m->curg->throwsplit) runtime·throw("runtime: stack split at bad time"); // The goroutine must be executing in order to call newstack, -- cgit v1.3 From ac9218f5f06dabec3ef7682619dd98fe587d6c08 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Tue, 30 Sep 2014 08:51:02 -0700 Subject: runtime: fix scanning of gc work buffer GC types were not being generated for the garbage collector work buffer. The markfor object was being collected as a result. This broke amd64p32 and maybe plan9 builds. Why it didn't break every build I'm not sure... Fixes #8812 LGTM=0intro, rsc R=golang-codereviews, dave, khr, 0intro, rsc CC=golang-codereviews https://golang.org/cl/149260043 --- src/runtime/mgc0.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c index c92fa1db73..9b9bc0ef13 100644 --- a/src/runtime/mgc0.c +++ b/src/runtime/mgc0.c @@ -140,7 +140,8 @@ static BitVector unrollglobgcprog(byte *prog, uintptr size); void runtime·bgsweep(void); static FuncVal bgsweepv = {runtime·bgsweep}; -struct { +typedef struct WorkData WorkData; +struct WorkData { uint64 full; // lock-free list of full blocks uint64 empty; // lock-free list of empty blocks byte pad0[CacheLineSize]; // prevents false-sharing between full/empty and nproc/nwait @@ -154,7 +155,8 @@ struct { // Copy of mheap.allspans for marker or sweeper. MSpan** spans; uint32 nspan; -} runtime·work; +}; +WorkData runtime·work; // scanblock scans a block of n bytes starting at pointer b for references // to other objects, scanning any it finds recursively until there are no @@ -1038,7 +1040,8 @@ runtime·MSpan_Sweep(MSpan *s, bool preserve) // State of background runtime·sweep. // Protected by runtime·gclock. -struct +typedef struct SweepData SweepData; +struct SweepData { G* g; bool parked; @@ -1047,7 +1050,8 @@ struct uint32 nbgsweep; uint32 npausesweep; -} runtime·sweep; +}; +SweepData runtime·sweep; // sweeps one span // returns number of pages returned to heap, or -1 if there is nothing to sweep -- cgit v1.3 From 9b2b0c8c1663330c64c6cec8feb413f4cf464348 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 30 Sep 2014 12:08:09 -0400 Subject: regexp/syntax: reject large repetitions created by nesting small ones Fixes #7609. LGTM=r R=r CC=golang-codereviews https://golang.org/cl/150270043 --- src/regexp/syntax/parse.go | 34 ++++++++++++++++++++++++++++++++++ src/regexp/syntax/parse_test.go | 13 +++++++++++++ 2 files changed, 47 insertions(+) (limited to 'src') diff --git a/src/regexp/syntax/parse.go b/src/regexp/syntax/parse.go index 08f8d307ae..3dc8ccf503 100644 --- a/src/regexp/syntax/parse.go +++ b/src/regexp/syntax/parse.go @@ -244,6 +244,7 @@ func (p *parser) repeat(op Op, min, max int, before, after, lastRepeat string) ( if sub.Op >= opPseudo { return "", &Error{ErrMissingRepeatArgument, before[:len(before)-len(after)]} } + re := p.newRegexp(op) re.Min = min re.Max = max @@ -251,9 +252,42 @@ func (p *parser) repeat(op Op, min, max int, before, after, lastRepeat string) ( re.Sub = re.Sub0[:1] re.Sub[0] = sub p.stack[n-1] = re + + if op == OpRepeat && (min >= 2 || max >= 2) && !repeatIsValid(re, 1000) { + return "", &Error{ErrInvalidRepeatSize, before[:len(before)-len(after)]} + } + return after, nil } +// repeatIsValid reports whether the repetition re is valid. +// Valid means that the combination of the top-level repetition +// and any inner repetitions does not exceed n copies of the +// innermost thing. +// This function rewalks the regexp tree and is called for every repetition, +// so we have to worry about inducing quadratic behavior in the parser. +// We avoid this by only calling repeatIsValid when min or max >= 2. +// In that case the depth of any >= 2 nesting can only get to 9 without +// triggering a parse error, so each subtree can only be rewalked 9 times. +func repeatIsValid(re *Regexp, n int) bool { + if re.Op == OpRepeat { + m := re.Max + if m < 0 { + m = re.Min + } + if m > n { + return false + } + n /= m + } + for _, sub := range re.Sub { + if !repeatIsValid(sub, n) { + return false + } + } + return true +} + // concat replaces the top of the stack (above the topmost '|' or '(') with its concatenation. func (p *parser) concat() *Regexp { p.maybeConcat(-1, 0) diff --git a/src/regexp/syntax/parse_test.go b/src/regexp/syntax/parse_test.go index f3089294c6..c4a1117ff8 100644 --- a/src/regexp/syntax/parse_test.go +++ b/src/regexp/syntax/parse_test.go @@ -200,6 +200,10 @@ var parseTests = []parseTest{ `cat{rep{2,2 lit{x}}alt{emp{}cc{0x30-0x39}}}`}, {`x{2}y|x{2}[0-9]y`, `cat{rep{2,2 lit{x}}alt{lit{y}cat{cc{0x30-0x39}lit{y}}}}`}, + + // Valid repetitions. + {`((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}))`, ``}, + {`((((((((((x{1}){2}){2}){2}){2}){2}){2}){2}){2}){2})`, ``}, } const testFlags = MatchNL | PerlX | UnicodeGroups @@ -262,6 +266,10 @@ func testParseDump(t *testing.T, tests []parseTest, flags Flags) { t.Errorf("Parse(%#q): %v", tt.Regexp, err) continue } + if tt.Dump == "" { + // It parsed. That's all we care about. + continue + } d := dump(re) if d != tt.Dump { t.Errorf("Parse(%#q).Dump() = %#q want %#q", tt.Regexp, d, tt.Dump) @@ -470,6 +478,7 @@ var invalidRegexps = []string{ `(?i)[a-Z]`, `a{100000}`, `a{100000,}`, + "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})", } var onlyPerl = []string{ @@ -527,6 +536,10 @@ func TestToStringEquivalentParse(t *testing.T) { t.Errorf("Parse(%#q): %v", tt.Regexp, err) continue } + if tt.Dump == "" { + // It parsed. That's all we care about. + continue + } d := dump(re) if d != tt.Dump { t.Errorf("Parse(%#q).Dump() = %#q want %#q", tt.Regexp, d, tt.Dump) -- cgit v1.3 From c75f81f0edd73d5f7d6528cec795b49c5d205c0c Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 30 Sep 2014 12:28:24 -0400 Subject: cmd/objdump: move armasm, x86asm into internal packages For Go 1.3 these external packages were collapsed into large single-file implementations stored in the cmd/objdump directory. For Go 1.4 we want pprof to be able to link against them too, so move them into cmd/internal, where they can be shared. The new files are copied from the repo in the file path (rsc.io/...). Those repos were code reviewed during development (mainly by crawshaw and minux), because we knew the main repo would use them. Update #8798 LGTM=bradfitz R=crawshaw, bradfitz CC=golang-codereviews https://golang.org/cl/153750044 --- src/cmd/internal/rsc.io/arm/armasm/Makefile | 2 + src/cmd/internal/rsc.io/arm/armasm/decode.go | 567 + src/cmd/internal/rsc.io/arm/armasm/decode_test.go | 69 + src/cmd/internal/rsc.io/arm/armasm/ext_test.go | 614 + src/cmd/internal/rsc.io/arm/armasm/gnu.go | 164 + src/cmd/internal/rsc.io/arm/armasm/inst.go | 438 + src/cmd/internal/rsc.io/arm/armasm/objdump_test.go | 258 + .../internal/rsc.io/arm/armasm/objdumpext_test.go | 260 + src/cmd/internal/rsc.io/arm/armasm/plan9x.go | 211 + src/cmd/internal/rsc.io/arm/armasm/tables.go | 9448 +++++++++++++ .../internal/rsc.io/arm/armasm/testdata/Makefile | 5 + .../internal/rsc.io/arm/armasm/testdata/decode.txt | 306 + src/cmd/internal/rsc.io/x86/x86asm/Makefile | 3 + src/cmd/internal/rsc.io/x86/x86asm/decode.go | 1616 +++ src/cmd/internal/rsc.io/x86/x86asm/decode_test.go | 71 + src/cmd/internal/rsc.io/x86/x86asm/ext_test.go | 811 ++ src/cmd/internal/rsc.io/x86/x86asm/gnu.go | 926 ++ src/cmd/internal/rsc.io/x86/x86asm/inst.go | 641 + src/cmd/internal/rsc.io/x86/x86asm/inst_test.go | 20 + src/cmd/internal/rsc.io/x86/x86asm/intel.go | 518 + src/cmd/internal/rsc.io/x86/x86asm/objdump_test.go | 383 + .../internal/rsc.io/x86/x86asm/objdumpext_test.go | 314 + .../internal/rsc.io/x86/x86asm/plan9ext_test.go | 120 + src/cmd/internal/rsc.io/x86/x86asm/plan9x.go | 346 + src/cmd/internal/rsc.io/x86/x86asm/plan9x_test.go | 54 + src/cmd/internal/rsc.io/x86/x86asm/tables.go | 9760 +++++++++++++ .../internal/rsc.io/x86/x86asm/testdata/Makefile | 12 + .../internal/rsc.io/x86/x86asm/testdata/decode.txt | 6731 +++++++++ src/cmd/internal/rsc.io/x86/x86asm/xed_test.go | 211 + src/cmd/internal/rsc.io/x86/x86asm/xedext_test.go | 206 + src/cmd/objdump/armasm.go | 10821 --------------- src/cmd/objdump/main.go | 11 +- src/cmd/objdump/x86.go | 13800 ------------------- 33 files changed, 35092 insertions(+), 24625 deletions(-) create mode 100644 src/cmd/internal/rsc.io/arm/armasm/Makefile create mode 100644 src/cmd/internal/rsc.io/arm/armasm/decode.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/decode_test.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/ext_test.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/gnu.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/inst.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/objdump_test.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/objdumpext_test.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/plan9x.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/tables.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/testdata/Makefile create mode 100644 src/cmd/internal/rsc.io/arm/armasm/testdata/decode.txt create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/Makefile create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/decode.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/decode_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/ext_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/gnu.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/inst.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/inst_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/intel.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/objdump_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/objdumpext_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/plan9ext_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/plan9x.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/plan9x_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/tables.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/testdata/Makefile create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/testdata/decode.txt create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/xed_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/xedext_test.go delete mode 100644 src/cmd/objdump/armasm.go delete mode 100644 src/cmd/objdump/x86.go (limited to 'src') diff --git a/src/cmd/internal/rsc.io/arm/armasm/Makefile b/src/cmd/internal/rsc.io/arm/armasm/Makefile new file mode 100644 index 0000000000..a3f57001f6 --- /dev/null +++ b/src/cmd/internal/rsc.io/arm/armasm/Makefile @@ -0,0 +1,2 @@ +tables.go: ../armmap/map.go ../arm.csv + go run ../armmap/map.go -fmt=decoder ../arm.csv >_tables.go && gofmt _tables.go >tables.go && rm _tables.go diff --git a/src/cmd/internal/rsc.io/arm/armasm/decode.go b/src/cmd/internal/rsc.io/arm/armasm/decode.go new file mode 100644 index 0000000000..6b4d73841b --- /dev/null +++ b/src/cmd/internal/rsc.io/arm/armasm/decode.go @@ -0,0 +1,567 @@ +// Copyright 2014 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 armasm + +import ( + "encoding/binary" + "fmt" +) + +// An instFormat describes the format of an instruction encoding. +// An instruction with 32-bit value x matches the format if x&mask == value +// and the condition matches. +// The condition matches if x>>28 == 0xF && value>>28==0xF +// or if x>>28 != 0xF and value>>28 == 0. +// If x matches the format, then the rest of the fields describe how to interpret x. +// The opBits describe bits that should be extracted from x and added to the opcode. +// For example opBits = 0x1234 means that the value +// (2 bits at offset 1) followed by (4 bits at offset 3) +// should be added to op. +// Finally the args describe how to decode the instruction arguments. +// args is stored as a fixed-size array; if there are fewer than len(args) arguments, +// args[i] == 0 marks the end of the argument list. +type instFormat struct { + mask uint32 + value uint32 + priority int8 + op Op + opBits uint64 + args instArgs +} + +type instArgs [4]instArg + +var ( + errMode = fmt.Errorf("unsupported execution mode") + errShort = fmt.Errorf("truncated instruction") + errUnknown = fmt.Errorf("unknown instruction") +) + +var decoderCover []bool + +// Decode decodes the leading bytes in src as a single instruction. +func Decode(src []byte, mode Mode) (inst Inst, err error) { + if mode != ModeARM { + return Inst{}, errMode + } + if len(src) < 4 { + return Inst{}, errShort + } + + if decoderCover == nil { + decoderCover = make([]bool, len(instFormats)) + } + + x := binary.LittleEndian.Uint32(src) + + // The instFormat table contains both conditional and unconditional instructions. + // Considering only the top 4 bits, the conditional instructions use mask=0, value=0, + // while the unconditional instructions use mask=f, value=f. + // Prepare a version of x with the condition cleared to 0 in conditional instructions + // and then assume mask=f during matching. + const condMask = 0xf0000000 + xNoCond := x + if x&condMask != condMask { + xNoCond &^= condMask + } + var priority int8 +Search: + for i := range instFormats { + f := &instFormats[i] + if xNoCond&(f.mask|condMask) != f.value || f.priority <= priority { + continue + } + delta := uint32(0) + deltaShift := uint(0) + for opBits := f.opBits; opBits != 0; opBits >>= 16 { + n := uint(opBits & 0xFF) + off := uint((opBits >> 8) & 0xFF) + delta |= (x >> off) & (1<> 8) & (1<<4 - 1)) + case arg_R_12: + return Reg((x >> 12) & (1<<4 - 1)) + case arg_R_16: + return Reg((x >> 16) & (1<<4 - 1)) + + case arg_R_12_nzcv: + r := Reg((x >> 12) & (1<<4 - 1)) + if r == R15 { + return APSR_nzcv + } + return r + + case arg_R_16_WB: + mode := AddrLDM + if (x>>21)&1 != 0 { + mode = AddrLDM_WB + } + return Mem{Base: Reg((x >> 16) & (1<<4 - 1)), Mode: mode} + + case arg_R_rotate: + Rm := Reg(x & (1<<4 - 1)) + typ, count := decodeShift(x) + // ROR #0 here means ROR #0, but decodeShift rewrites to RRX #1. + if typ == RotateRightExt { + return Reg(Rm) + } + return RegShift{Rm, typ, uint8(count)} + + case arg_R_shift_R: + Rm := Reg(x & (1<<4 - 1)) + Rs := Reg((x >> 8) & (1<<4 - 1)) + typ := Shift((x >> 5) & (1<<2 - 1)) + return RegShiftReg{Rm, typ, Rs} + + case arg_R_shift_imm: + Rm := Reg(x & (1<<4 - 1)) + typ, count := decodeShift(x) + if typ == ShiftLeft && count == 0 { + return Reg(Rm) + } + return RegShift{Rm, typ, uint8(count)} + + case arg_R1_0: + return Reg((x & (1<<4 - 1))) + case arg_R1_12: + return Reg(((x >> 12) & (1<<4 - 1))) + case arg_R2_0: + return Reg((x & (1<<4 - 1)) | 1) + case arg_R2_12: + return Reg(((x >> 12) & (1<<4 - 1)) | 1) + + case arg_SP: + return SP + + case arg_Sd_Dd: + v := (x >> 12) & (1<<4 - 1) + vx := (x >> 22) & 1 + sz := (x >> 8) & 1 + if sz != 0 { + return D0 + Reg(vx<<4+v) + } else { + return S0 + Reg(v<<1+vx) + } + + case arg_Dd_Sd: + return decodeArg(arg_Sd_Dd, x^(1<<8)) + + case arg_Sd: + v := (x >> 12) & (1<<4 - 1) + vx := (x >> 22) & 1 + return S0 + Reg(v<<1+vx) + + case arg_Sm_Dm: + v := (x >> 0) & (1<<4 - 1) + vx := (x >> 5) & 1 + sz := (x >> 8) & 1 + if sz != 0 { + return D0 + Reg(vx<<4+v) + } else { + return S0 + Reg(v<<1+vx) + } + + case arg_Sm: + v := (x >> 0) & (1<<4 - 1) + vx := (x >> 5) & 1 + return S0 + Reg(v<<1+vx) + + case arg_Dn_half: + v := (x >> 16) & (1<<4 - 1) + vx := (x >> 7) & 1 + return RegX{D0 + Reg(vx<<4+v), int((x >> 21) & 1)} + + case arg_Sn_Dn: + v := (x >> 16) & (1<<4 - 1) + vx := (x >> 7) & 1 + sz := (x >> 8) & 1 + if sz != 0 { + return D0 + Reg(vx<<4+v) + } else { + return S0 + Reg(v<<1+vx) + } + + case arg_Sn: + v := (x >> 16) & (1<<4 - 1) + vx := (x >> 7) & 1 + return S0 + Reg(v<<1+vx) + + case arg_const: + v := x & (1<<8 - 1) + rot := (x >> 8) & (1<<4 - 1) * 2 + if rot > 0 && v&3 == 0 { + // could rotate less + return ImmAlt{uint8(v), uint8(rot)} + } + if rot >= 24 && ((v<<(32-rot))&0xFF)>>(32-rot) == v { + // could wrap around to rot==0. + return ImmAlt{uint8(v), uint8(rot)} + } + return Imm(v>>rot | v<<(32-rot)) + + case arg_endian: + return Endian((x >> 9) & 1) + + case arg_fbits: + return Imm((16 << ((x >> 7) & 1)) - ((x&(1<<4-1))<<1 | (x>>5)&1)) + + case arg_fp_0: + return Imm(0) + + case arg_imm24: + return Imm(x & (1<<24 - 1)) + + case arg_imm5: + return Imm((x >> 7) & (1<<5 - 1)) + + case arg_imm5_32: + x = (x >> 7) & (1<<5 - 1) + if x == 0 { + x = 32 + } + return Imm(x) + + case arg_imm5_nz: + x = (x >> 7) & (1<<5 - 1) + if x == 0 { + return nil + } + return Imm(x) + + case arg_imm_4at16_12at0: + return Imm((x>>16)&(1<<4-1)<<12 | x&(1<<12-1)) + + case arg_imm_12at8_4at0: + return Imm((x>>8)&(1<<12-1)<<4 | x&(1<<4-1)) + + case arg_imm_vfp: + x = (x>>16)&(1<<4-1)<<4 | x&(1<<4-1) + return Imm(x) + + case arg_label24: + imm := (x & (1<<24 - 1)) << 2 + return PCRel(int32(imm<<6) >> 6) + + case arg_label24H: + h := (x >> 24) & 1 + imm := (x&(1<<24-1))<<2 | h<<1 + return PCRel(int32(imm<<6) >> 6) + + case arg_label_m_12: + d := int32(x & (1<<12 - 1)) + return Mem{Base: PC, Mode: AddrOffset, Offset: int16(-d)} + + case arg_label_p_12: + d := int32(x & (1<<12 - 1)) + return Mem{Base: PC, Mode: AddrOffset, Offset: int16(d)} + + case arg_label_pm_12: + d := int32(x & (1<<12 - 1)) + u := (x >> 23) & 1 + if u == 0 { + d = -d + } + return Mem{Base: PC, Mode: AddrOffset, Offset: int16(d)} + + case arg_label_pm_4_4: + d := int32((x>>8)&(1<<4-1)<<4 | x&(1<<4-1)) + u := (x >> 23) & 1 + if u == 0 { + d = -d + } + return PCRel(d) + + case arg_lsb_width: + lsb := (x >> 7) & (1<<5 - 1) + msb := (x >> 16) & (1<<5 - 1) + if msb < lsb || msb >= 32 { + return nil + } + return Imm(msb + 1 - lsb) + + case arg_mem_R: + Rn := Reg((x >> 16) & (1<<4 - 1)) + return Mem{Base: Rn, Mode: AddrOffset} + + case arg_mem_R_pm_R_postindex: + // Treat [],+/- like [,+/-{,}]{!} + // by forcing shift bits to <<0 and P=0, W=0 (postindex=true). + return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^((1<<7-1)<<5|1<<24|1<<21)) + + case arg_mem_R_pm_R_W: + // Treat [,+/-]{!} like [,+/-{,}]{!} + // by forcing shift bits to <<0. + return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^((1<<7-1)<<5)) + + case arg_mem_R_pm_R_shift_imm_offset: + // Treat [],+/-{,} like [,+/-{,}]{!} + // by forcing P=1, W=0 (index=false, wback=false). + return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^(1<<21)|1<<24) + + case arg_mem_R_pm_R_shift_imm_postindex: + // Treat [],+/-{,} like [,+/-{,}]{!} + // by forcing P=0, W=0 (postindex=true). + return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^(1<<24|1<<21)) + + case arg_mem_R_pm_R_shift_imm_W: + Rn := Reg((x >> 16) & (1<<4 - 1)) + Rm := Reg(x & (1<<4 - 1)) + typ, count := decodeShift(x) + u := (x >> 23) & 1 + w := (x >> 21) & 1 + p := (x >> 24) & 1 + if p == 0 && w == 1 { + return nil + } + sign := int8(+1) + if u == 0 { + sign = -1 + } + mode := AddrMode(uint8(p<<1) | uint8(w^1)) + return Mem{Base: Rn, Mode: mode, Sign: sign, Index: Rm, Shift: typ, Count: count} + + case arg_mem_R_pm_imm12_offset: + // Treat [,#+/-] like [{,#+/-}]{!} + // by forcing P=1, W=0 (index=false, wback=false). + return decodeArg(arg_mem_R_pm_imm12_W, x&^(1<<21)|1<<24) + + case arg_mem_R_pm_imm12_postindex: + // Treat [],#+/- like [{,#+/-}]{!} + // by forcing P=0, W=0 (postindex=true). + return decodeArg(arg_mem_R_pm_imm12_W, x&^(1<<24|1<<21)) + + case arg_mem_R_pm_imm12_W: + Rn := Reg((x >> 16) & (1<<4 - 1)) + u := (x >> 23) & 1 + w := (x >> 21) & 1 + p := (x >> 24) & 1 + if p == 0 && w == 1 { + return nil + } + sign := int8(+1) + if u == 0 { + sign = -1 + } + imm := int16(x & (1<<12 - 1)) + mode := AddrMode(uint8(p<<1) | uint8(w^1)) + return Mem{Base: Rn, Mode: mode, Offset: int16(sign) * imm} + + case arg_mem_R_pm_imm8_postindex: + // Treat [],#+/- like [{,#+/-}]{!} + // by forcing P=0, W=0 (postindex=true). + return decodeArg(arg_mem_R_pm_imm8_W, x&^(1<<24|1<<21)) + + case arg_mem_R_pm_imm8_W: + Rn := Reg((x >> 16) & (1<<4 - 1)) + u := (x >> 23) & 1 + w := (x >> 21) & 1 + p := (x >> 24) & 1 + if p == 0 && w == 1 { + return nil + } + sign := int8(+1) + if u == 0 { + sign = -1 + } + imm := int16((x>>8)&(1<<4-1)<<4 | x&(1<<4-1)) + mode := AddrMode(uint8(p<<1) | uint8(w^1)) + return Mem{Base: Rn, Mode: mode, Offset: int16(sign) * imm} + + case arg_mem_R_pm_imm8at0_offset: + Rn := Reg((x >> 16) & (1<<4 - 1)) + u := (x >> 23) & 1 + sign := int8(+1) + if u == 0 { + sign = -1 + } + imm := int16(x&(1<<8-1)) << 2 + return Mem{Base: Rn, Mode: AddrOffset, Offset: int16(sign) * imm} + + case arg_option: + return Imm(x & (1<<4 - 1)) + + case arg_registers: + return RegList(x & (1<<16 - 1)) + + case arg_registers2: + x &= 1<<16 - 1 + n := 0 + for i := 0; i < 16; i++ { + if x>>uint(i)&1 != 0 { + n++ + } + } + if n < 2 { + return nil + } + return RegList(x) + + case arg_registers1: + Rt := (x >> 12) & (1<<4 - 1) + return RegList(1 << Rt) + + case arg_satimm4: + return Imm((x >> 16) & (1<<4 - 1)) + + case arg_satimm5: + return Imm((x >> 16) & (1<<5 - 1)) + + case arg_satimm4m1: + return Imm((x>>16)&(1<<4-1) + 1) + + case arg_satimm5m1: + return Imm((x>>16)&(1<<5-1) + 1) + + case arg_widthm1: + return Imm((x>>16)&(1<<5-1) + 1) + + } +} + +// decodeShift decodes the shift-by-immediate encoded in x. +func decodeShift(x uint32) (Shift, uint8) { + count := (x >> 7) & (1<<5 - 1) + typ := Shift((x >> 5) & (1<<2 - 1)) + switch typ { + case ShiftRight, ShiftRightSigned: + if count == 0 { + count = 32 + } + case RotateRight: + if count == 0 { + typ = RotateRightExt + count = 1 + } + } + return typ, uint8(count) +} diff --git a/src/cmd/internal/rsc.io/arm/armasm/decode_test.go b/src/cmd/internal/rsc.io/arm/armasm/decode_test.go new file mode 100644 index 0000000000..25a345a882 --- /dev/null +++ b/src/cmd/internal/rsc.io/arm/armasm/decode_test.go @@ -0,0 +1,69 @@ +// Copyright 2014 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 armasm + +import ( + "encoding/hex" + "io/ioutil" + "strconv" + "strings" + "testing" +) + +func TestDecode(t *testing.T) { + data, err := ioutil.ReadFile("testdata/decode.txt") + if err != nil { + t.Fatal(err) + } + all := string(data) + for strings.Contains(all, "\t\t") { + all = strings.Replace(all, "\t\t", "\t", -1) + } + for _, line := range strings.Split(all, "\n") { + line = strings.TrimSpace(line) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + f := strings.SplitN(line, "\t", 4) + i := strings.Index(f[0], "|") + if i < 0 { + t.Errorf("parsing %q: missing | separator", f[0]) + continue + } + if i%2 != 0 { + t.Errorf("parsing %q: misaligned | separator", f[0]) + } + size := i / 2 + code, err := hex.DecodeString(f[0][:i] + f[0][i+1:]) + if err != nil { + t.Errorf("parsing %q: %v", f[0], err) + continue + } + mode, err := strconv.Atoi(f[1]) + if err != nil { + t.Errorf("invalid mode %q in: %s", f[1], line) + continue + } + syntax, asm := f[2], f[3] + inst, err := Decode(code, Mode(mode)) + var out string + if err != nil { + out = "error: " + err.Error() + } else { + switch syntax { + case "gnu": + out = GNUSyntax(inst) + case "plan9": + out = Plan9Syntax(inst, 0, nil, nil) + default: + t.Errorf("unknown syntax %q", syntax) + continue + } + } + if out != asm || inst.Len != size { + t.Errorf("Decode(%s) [%s] = %s, %d, want %s, %d", f[0], syntax, out, inst.Len, asm, size) + } + } +} diff --git a/src/cmd/internal/rsc.io/arm/armasm/ext_test.go b/src/cmd/internal/rsc.io/arm/armasm/ext_test.go new file mode 100644 index 0000000000..b0bd855970 --- /dev/null +++ b/src/cmd/internal/rsc.io/arm/armasm/ext_test.go @@ -0,0 +1,614 @@ +// Copyright 2014 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. + +// Support for testing against external disassembler program. +// Copied and simplified from rsc.io/x86/x86asm/ext_test.go. + +package armasm + +import ( + "bufio" + "bytes" + "encoding/hex" + "flag" + "fmt" + "io/ioutil" + "log" + "math/rand" + "os" + "os/exec" + "regexp" + "runtime" + "strings" + "testing" + "time" +) + +var ( + printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths") + dumpTest = flag.Bool("dump", false, "dump all encodings") + mismatch = flag.Bool("mismatch", false, "log allowed mismatches") + longTest = flag.Bool("long", false, "long test") + keep = flag.Bool("keep", false, "keep object files around") + debug = false +) + +// A ExtInst represents a single decoded instruction parsed +// from an external disassembler's output. +type ExtInst struct { + addr uint32 + enc [4]byte + nenc int + text string +} + +func (r ExtInst) String() string { + return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text) +} + +// An ExtDis is a connection between an external disassembler and a test. +type ExtDis struct { + Arch Mode + Dec chan ExtInst + File *os.File + Size int + KeepFile bool + Cmd *exec.Cmd +} + +// Run runs the given command - the external disassembler - and returns +// a buffered reader of its standard output. +func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) { + if *keep { + log.Printf("%s\n", strings.Join(cmd, " ")) + } + ext.Cmd = exec.Command(cmd[0], cmd[1:]...) + out, err := ext.Cmd.StdoutPipe() + if err != nil { + return nil, fmt.Errorf("stdoutpipe: %v", err) + } + if err := ext.Cmd.Start(); err != nil { + return nil, fmt.Errorf("exec: %v", err) + } + + b := bufio.NewReaderSize(out, 1<<20) + return b, nil +} + +// Wait waits for the command started with Run to exit. +func (ext *ExtDis) Wait() error { + return ext.Cmd.Wait() +} + +// testExtDis tests a set of byte sequences against an external disassembler. +// The disassembler is expected to produce the given syntax and be run +// in the given architecture mode (16, 32, or 64-bit). +// The extdis function must start the external disassembler +// and then parse its output, sending the parsed instructions on ext.Dec. +// The generate function calls its argument f once for each byte sequence +// to be tested. The generate function itself will be called twice, and it must +// make the same sequence of calls to f each time. +// When a disassembly does not match the internal decoding, +// allowedMismatch determines whether this mismatch should be +// allowed, or else considered an error. +func testExtDis( + t *testing.T, + syntax string, + arch Mode, + extdis func(ext *ExtDis) error, + generate func(f func([]byte)), + allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool, +) { + start := time.Now() + ext := &ExtDis{ + Dec: make(chan ExtInst), + Arch: arch, + } + errc := make(chan error) + + // First pass: write instructions to input file for external disassembler. + file, f, size, err := writeInst(generate) + if err != nil { + t.Fatal(err) + } + ext.Size = size + ext.File = f + defer func() { + f.Close() + if !*keep { + os.Remove(file) + } + }() + + // Second pass: compare disassembly against our decodings. + var ( + totalTests = 0 + totalSkips = 0 + totalErrors = 0 + + errors = make([]string, 0, 100) // sampled errors, at most cap + ) + go func() { + errc <- extdis(ext) + }() + generate(func(enc []byte) { + dec, ok := <-ext.Dec + if !ok { + t.Errorf("decoding stream ended early") + return + } + inst, text := disasm(syntax, arch, pad(enc)) + totalTests++ + if *dumpTest { + fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc) + } + if text != dec.text || inst.Len != dec.nenc { + suffix := "" + if allowedMismatch(text, size, &inst, dec) { + totalSkips++ + if !*mismatch { + return + } + suffix += " (allowed mismatch)" + } + totalErrors++ + if len(errors) >= cap(errors) { + j := rand.Intn(totalErrors) + if j >= cap(errors) { + return + } + errors = append(errors[:j], errors[j+1:]...) + } + errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix)) + } + }) + + if *mismatch { + totalErrors -= totalSkips + } + + for _, b := range errors { + t.Log(b) + } + + if totalErrors > 0 { + t.Fail() + } + t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds()) + + if err := <-errc; err != nil { + t.Fatal("external disassembler: %v", err) + } + +} + +const start = 0x8000 // start address of text + +// writeInst writes the generated byte sequences to a new file +// starting at offset start. That file is intended to be the input to +// the external disassembler. +func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) { + f, err = ioutil.TempFile("", "armasm") + if err != nil { + return + } + + file = f.Name() + + f.Seek(start, 0) + w := bufio.NewWriter(f) + defer w.Flush() + size = 0 + generate(func(x []byte) { + if len(x) > 4 { + x = x[:4] + } + if debug { + fmt.Printf("%#x: %x%x\n", start+size, x, zeros[len(x):]) + } + w.Write(x) + w.Write(zeros[len(x):]) + size += len(zeros) + }) + return file, f, size, nil +} + +var zeros = []byte{0, 0, 0, 0} + +// pad pads the code sequenc with pops. +func pad(enc []byte) []byte { + if len(enc) < 4 { + enc = append(enc[:len(enc):len(enc)], zeros[:4-len(enc)]...) + } + return enc +} + +// disasm returns the decoded instruction and text +// for the given source bytes, using the given syntax and mode. +func disasm(syntax string, mode Mode, src []byte) (inst Inst, text string) { + // If printTests is set, we record the coverage value + // before and after, and we write out the inputs for which + // coverage went up, in the format expected in testdata/decode.text. + // This produces a fairly small set of test cases that exercise nearly + // all the code. + var cover float64 + if *printTests { + cover -= coverage() + } + + inst, err := Decode(src, mode) + if err != nil { + text = "error: " + err.Error() + } else { + text = inst.String() + switch syntax { + //case "arm": + // text = ARMSyntax(inst) + case "gnu": + text = GNUSyntax(inst) + //case "plan9": + // text = Plan9Syntax(inst, 0, nil) + default: + text = "error: unknown syntax " + syntax + } + } + + if *printTests { + cover += coverage() + if cover > 0 { + max := len(src) + if max > 4 && inst.Len <= 4 { + max = 4 + } + fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], mode, syntax, text) + } + } + + return +} + +// coverage returns a floating point number denoting the +// test coverage until now. The number increases when new code paths are exercised, +// both in the Go program and in the decoder byte code. +func coverage() float64 { + /* + testing.Coverage is not in the main distribution. + The implementation, which must go in package testing, is: + + // Coverage reports the current code coverage as a fraction in the range [0, 1]. + func Coverage() float64 { + var n, d int64 + for _, counters := range cover.Counters { + for _, c := range counters { + if c > 0 { + n++ + } + d++ + } + } + if d == 0 { + return 0 + } + return float64(n) / float64(d) + } + */ + + var f float64 + f += testing.Coverage() + f += decodeCoverage() + return f +} + +func decodeCoverage() float64 { + n := 0 + for _, t := range decoderCover { + if t { + n++ + } + } + return float64(1+n) / float64(1+len(decoderCover)) +} + +// Helpers for writing disassembler output parsers. + +// hasPrefix reports whether any of the space-separated words in the text s +// begins with any of the given prefixes. +func hasPrefix(s string, prefixes ...string) bool { + for _, prefix := range prefixes { + for s := s; s != ""; { + if strings.HasPrefix(s, prefix) { + return true + } + i := strings.Index(s, " ") + if i < 0 { + break + } + s = s[i+1:] + } + } + return false +} + +// contains reports whether the text s contains any of the given substrings. +func contains(s string, substrings ...string) bool { + for _, sub := range substrings { + if strings.Contains(s, sub) { + return true + } + } + return false +} + +// isHex reports whether b is a hexadecimal character (0-9A-Fa-f). +func isHex(b byte) bool { return b == '0' || unhex[b] > 0 } + +// parseHex parses the hexadecimal byte dump in hex, +// appending the parsed bytes to raw and returning the updated slice. +// The returned bool signals whether any invalid hex was found. +// Spaces and tabs between bytes are okay but any other non-hex is not. +func parseHex(hex []byte, raw []byte) ([]byte, bool) { + hex = trimSpace(hex) + for j := 0; j < len(hex); { + for hex[j] == ' ' || hex[j] == '\t' { + j++ + } + if j >= len(hex) { + break + } + if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) { + return nil, false + } + raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]]) + j += 2 + } + return raw, true +} + +var unhex = [256]byte{ + '0': 0, + '1': 1, + '2': 2, + '3': 3, + '4': 4, + '5': 5, + '6': 6, + '7': 7, + '8': 8, + '9': 9, + 'A': 10, + 'B': 11, + 'C': 12, + 'D': 13, + 'E': 14, + 'F': 15, + 'a': 10, + 'b': 11, + 'c': 12, + 'd': 13, + 'e': 14, + 'f': 15, +} + +// index is like bytes.Index(s, []byte(t)) but avoids the allocation. +func index(s []byte, t string) int { + i := 0 + for { + j := bytes.IndexByte(s[i:], t[0]) + if j < 0 { + return -1 + } + i = i + j + if i+len(t) > len(s) { + return -1 + } + for k := 1; k < len(t); k++ { + if s[i+k] != t[k] { + goto nomatch + } + } + return i + nomatch: + i++ + } +} + +// fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s. +// If s must be rewritten, it is rewritten in place. +func fixSpace(s []byte) []byte { + s = trimSpace(s) + for i := 0; i < len(s); i++ { + if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' { + goto Fix + } + } + return s + +Fix: + b := s + w := 0 + for i := 0; i < len(s); i++ { + c := s[i] + if c == '\t' || c == '\n' { + c = ' ' + } + if c == ' ' && w > 0 && b[w-1] == ' ' { + continue + } + b[w] = c + w++ + } + if w > 0 && b[w-1] == ' ' { + w-- + } + return b[:w] +} + +// trimSpace trims leading and trailing space from s, returning a subslice of s. +func trimSpace(s []byte) []byte { + j := len(s) + for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') { + j-- + } + i := 0 + for i < j && (s[i] == ' ' || s[i] == '\t') { + i++ + } + return s[i:j] +} + +// pcrel matches instructions using relative addressing mode. +var ( + pcrel = regexp.MustCompile(`^((?:.* )?(?:b|bl)x?(?:eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le)?) 0x([0-9a-f]+)$`) +) + +// Generators. +// +// The test cases are described as functions that invoke a callback repeatedly, +// with a new input sequence each time. These helpers make writing those +// a little easier. + +// condCases generates conditional instructions. +func condCases(t *testing.T) func(func([]byte)) { + return func(try func([]byte)) { + // All the strides are relatively prime to 2 and therefore to 2²⁸, + // so we will not repeat any instructions until we have tried all 2²⁸. + // Using a stride other than 1 is meant to visit the instructions in a + // pseudorandom order, which gives better variety in the set of + // test cases chosen by -printtests. + stride := uint32(10007) + n := 1 << 28 / 7 + if testing.Short() { + stride = 100003 + n = 1 << 28 / 1001 + } else if *longTest { + stride = 200000033 + n = 1 << 28 + } + x := uint32(0) + for i := 0; i < n; i++ { + enc := (x%15)<<28 | x&(1<<28-1) + try([]byte{byte(enc), byte(enc >> 8), byte(enc >> 16), byte(enc >> 24)}) + x += stride + } + } +} + +// uncondCases generates unconditional instructions. +func uncondCases(t *testing.T) func(func([]byte)) { + return func(try func([]byte)) { + condCases(t)(func(enc []byte) { + enc[3] |= 0xF0 + try(enc) + }) + } +} + +func countBits(x uint32) int { + n := 0 + for ; x != 0; x >>= 1 { + n += int(x & 1) + } + return n +} + +func expandBits(x, m uint32) uint32 { + var out uint32 + for i := uint(0); i < 32; i++ { + out >>= 1 + if m&1 != 0 { + out |= (x & 1) << 31 + x >>= 1 + } + m >>= 1 + } + return out +} + +func tryCondMask(mask, val uint32, try func([]byte)) { + n := countBits(^mask) + bits := uint32(0) + for i := 0; i < 1<> 8), byte(x >> 16), byte(x >> 24)}) + } +} + +// vfpCases generates VFP instructions. +func vfpCases(t *testing.T) func(func([]byte)) { + const ( + vfpmask uint32 = 0xFF00FE10 + vfp uint32 = 0x0E009A00 + ) + return func(try func([]byte)) { + tryCondMask(0xff00fe10, 0x0e009a00, try) // standard VFP instruction space + tryCondMask(0xffc00f7f, 0x0e000b10, try) // VFP MOV core reg to/from float64 half + tryCondMask(0xffe00f7f, 0x0e000a10, try) // VFP MOV core reg to/from float32 + tryCondMask(0xffef0fff, 0x0ee10a10, try) // VFP MOV core reg to/from cond codes + } +} + +// hexCases generates the cases written in hexadecimal in the encoded string. +// Spaces in 'encoded' separate entire test cases, not individual bytes. +func hexCases(t *testing.T, encoded string) func(func([]byte)) { + return func(try func([]byte)) { + for _, x := range strings.Fields(encoded) { + src, err := hex.DecodeString(x) + if err != nil { + t.Errorf("parsing %q: %v", x, err) + } + try(src) + } + } +} + +// testdataCases generates the test cases recorded in testdata/decode.txt. +// It only uses the inputs; it ignores the answers recorded in that file. +func testdataCases(t *testing.T) func(func([]byte)) { + var codes [][]byte + data, err := ioutil.ReadFile("testdata/decode.txt") + if err != nil { + t.Fatal(err) + } + for _, line := range strings.Split(string(data), "\n") { + line = strings.TrimSpace(line) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + f := strings.Fields(line)[0] + i := strings.Index(f, "|") + if i < 0 { + t.Errorf("parsing %q: missing | separator", f) + continue + } + if i%2 != 0 { + t.Errorf("parsing %q: misaligned | separator", f) + } + code, err := hex.DecodeString(f[:i] + f[i+1:]) + if err != nil { + t.Errorf("parsing %q: %v", f, err) + continue + } + codes = append(codes, code) + } + + return func(try func([]byte)) { + for _, code := range codes { + try(code) + } + } +} + +func caller(skip int) string { + pc, _, _, _ := runtime.Caller(skip) + f := runtime.FuncForPC(pc) + name := "?" + if f != nil { + name = f.Name() + if i := strings.LastIndex(name, "."); i >= 0 { + name = name[i+1:] + } + } + return name +} diff --git a/src/cmd/internal/rsc.io/arm/armasm/gnu.go b/src/cmd/internal/rsc.io/arm/armasm/gnu.go new file mode 100644 index 0000000000..1a97a5a844 --- /dev/null +++ b/src/cmd/internal/rsc.io/arm/armasm/gnu.go @@ -0,0 +1,164 @@ +// Copyright 2014 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 armasm + +import ( + "bytes" + "fmt" + "strings" +) + +var saveDot = strings.NewReplacer( + ".F16", "_dot_F16", + ".F32", "_dot_F32", + ".F64", "_dot_F64", + ".S32", "_dot_S32", + ".U32", "_dot_U32", + ".FXS", "_dot_S", + ".FXU", "_dot_U", + ".32", "_dot_32", +) + +// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils. +// This form typically matches the syntax defined in the ARM Reference Manual. +func GNUSyntax(inst Inst) string { + var buf bytes.Buffer + op := inst.Op.String() + op = saveDot.Replace(op) + op = strings.Replace(op, ".", "", -1) + op = strings.Replace(op, "_dot_", ".", -1) + op = strings.ToLower(op) + buf.WriteString(op) + sep := " " + for i, arg := range inst.Args { + if arg == nil { + break + } + text := gnuArg(&inst, i, arg) + if text == "" { + continue + } + buf.WriteString(sep) + sep = ", " + buf.WriteString(text) + } + return buf.String() +} + +func gnuArg(inst *Inst, argIndex int, arg Arg) string { + switch inst.Op &^ 15 { + case LDRD_EQ, LDREXD_EQ, STRD_EQ: + if argIndex == 1 { + // second argument in consecutive pair not printed + return "" + } + case STREXD_EQ: + if argIndex == 2 { + // second argument in consecutive pair not printed + return "" + } + } + + switch arg := arg.(type) { + case Imm: + switch inst.Op &^ 15 { + case BKPT_EQ: + return fmt.Sprintf("%#04x", uint32(arg)) + case SVC_EQ: + return fmt.Sprintf("%#08x", uint32(arg)) + } + return fmt.Sprintf("#%d", int32(arg)) + + case ImmAlt: + return fmt.Sprintf("#%d, %d", arg.Val, arg.Rot) + + case Mem: + R := gnuArg(inst, -1, arg.Base) + X := "" + if arg.Sign != 0 { + X = "" + if arg.Sign < 0 { + X = "-" + } + X += gnuArg(inst, -1, arg.Index) + if arg.Shift == ShiftLeft && arg.Count == 0 { + // nothing + } else if arg.Shift == RotateRightExt { + X += ", rrx" + } else { + X += fmt.Sprintf(", %s #%d", strings.ToLower(arg.Shift.String()), arg.Count) + } + } else { + X = fmt.Sprintf("#%d", arg.Offset) + } + + switch arg.Mode { + case AddrOffset: + if X == "#0" { + return fmt.Sprintf("[%s]", R) + } + return fmt.Sprintf("[%s, %s]", R, X) + case AddrPreIndex: + return fmt.Sprintf("[%s, %s]!", R, X) + case AddrPostIndex: + return fmt.Sprintf("[%s], %s", R, X) + case AddrLDM: + if X == "#0" { + return R + } + case AddrLDM_WB: + if X == "#0" { + return R + "!" + } + } + return fmt.Sprintf("[%s Mode(%d) %s]", R, int(arg.Mode), X) + + case PCRel: + return fmt.Sprintf(".%+#x", int32(arg)+4) + + case Reg: + switch inst.Op &^ 15 { + case LDREX_EQ: + if argIndex == 0 { + return fmt.Sprintf("r%d", int32(arg)) + } + } + switch arg { + case R10: + return "sl" + case R11: + return "fp" + case R12: + return "ip" + } + + case RegList: + var buf bytes.Buffer + fmt.Fprintf(&buf, "{") + sep := "" + for i := 0; i < 16; i++ { + if arg&(1<= Op(len(opstr)) || opstr[op] == "" { + return fmt.Sprintf("Op(%d)", int(op)) + } + return opstr[op] +} + +// An Inst is a single instruction. +type Inst struct { + Op Op // Opcode mnemonic + Enc uint32 // Raw encoding bits. + Len int // Length of encoding in bytes. + Args Args // Instruction arguments, in ARM manual order. +} + +func (i Inst) String() string { + var buf bytes.Buffer + buf.WriteString(i.Op.String()) + for j, arg := range i.Args { + if arg == nil { + break + } + if j == 0 { + buf.WriteString(" ") + } else { + buf.WriteString(", ") + } + buf.WriteString(arg.String()) + } + return buf.String() +} + +// An Args holds the instruction arguments. +// If an instruction has fewer than 4 arguments, +// the final elements in the array are nil. +type Args [4]Arg + +// An Arg is a single instruction argument, one of these types: +// Endian, Imm, Mem, PCRel, Reg, RegList, RegShift, RegShiftReg. +type Arg interface { + IsArg() + String() string +} + +type Float32Imm float32 + +func (Float32Imm) IsArg() {} + +func (f Float32Imm) String() string { + return fmt.Sprintf("#%v", float32(f)) +} + +type Float64Imm float32 + +func (Float64Imm) IsArg() {} + +func (f Float64Imm) String() string { + return fmt.Sprintf("#%v", float64(f)) +} + +// An Imm is an integer constant. +type Imm uint32 + +func (Imm) IsArg() {} + +func (i Imm) String() string { + return fmt.Sprintf("#%#x", uint32(i)) +} + +// A ImmAlt is an alternate encoding of an integer constant. +type ImmAlt struct { + Val uint8 + Rot uint8 +} + +func (ImmAlt) IsArg() {} + +func (i ImmAlt) Imm() Imm { + v := uint32(i.Val) + r := uint(i.Rot) + return Imm(v>>r | v<<(32-r)) +} + +func (i ImmAlt) String() string { + return fmt.Sprintf("#%#x, %d", i.Val, i.Rot) +} + +// A Label is a text (code) address. +type Label uint32 + +func (Label) IsArg() {} + +func (i Label) String() string { + return fmt.Sprintf("%#x", uint32(i)) +} + +// A Reg is a single register. +// The zero value denotes R0, not the absence of a register. +type Reg uint8 + +const ( + R0 Reg = iota + R1 + R2 + R3 + R4 + R5 + R6 + R7 + R8 + R9 + R10 + R11 + R12 + R13 + R14 + R15 + + S0 + S1 + S2 + S3 + S4 + S5 + S6 + S7 + S8 + S9 + S10 + S11 + S12 + S13 + S14 + S15 + S16 + S17 + S18 + S19 + S20 + S21 + S22 + S23 + S24 + S25 + S26 + S27 + S28 + S29 + S30 + S31 + + D0 + D1 + D2 + D3 + D4 + D5 + D6 + D7 + D8 + D9 + D10 + D11 + D12 + D13 + D14 + D15 + D16 + D17 + D18 + D19 + D20 + D21 + D22 + D23 + D24 + D25 + D26 + D27 + D28 + D29 + D30 + D31 + + APSR + APSR_nzcv + FPSCR + + SP = R13 + LR = R14 + PC = R15 +) + +func (Reg) IsArg() {} + +func (r Reg) String() string { + switch r { + case APSR: + return "APSR" + case APSR_nzcv: + return "APSR_nzcv" + case FPSCR: + return "FPSCR" + case SP: + return "SP" + case PC: + return "PC" + case LR: + return "LR" + } + if R0 <= r && r <= R15 { + return fmt.Sprintf("R%d", int(r-R0)) + } + if S0 <= r && r <= S31 { + return fmt.Sprintf("S%d", int(r-S0)) + } + if D0 <= r && r <= D31 { + return fmt.Sprintf("D%d", int(r-D0)) + } + return fmt.Sprintf("Reg(%d)", int(r)) +} + +// A RegX represents a fraction of a multi-value register. +// The Index field specifies the index number, +// but the size of the fraction is not specified. +// It must be inferred from the instruction and the register type. +// For example, in a VMOV instruction, RegX{D5, 1} represents +// the top 32 bits of the 64-bit D5 register. +type RegX struct { + Reg Reg + Index int +} + +func (RegX) IsArg() {} + +func (r RegX) String() string { + return fmt.Sprintf("%s[%d]", r.Reg, r.Index) +} + +// A RegList is a register list. +// Bits at indexes x = 0 through 15 indicate whether the corresponding Rx register is in the list. +type RegList uint16 + +func (RegList) IsArg() {} + +func (r RegList) String() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "{") + sep := "" + for i := 0; i < 16; i++ { + if r&(1<= 4 { + raw := binary.LittleEndian.Uint32(dec.enc[:4]) + + // word 21FFF0B5. + // the manual is clear that this is pre-indexed mode (with !) but libopcodes generates post-index (without !). + if raw&0x01200000 == 0x01200000 && strings.Replace(text, "!", "", -1) == dec.text { + return true + } + + // word C100543E: libopcodes says tst, but no evidence for that. + if strings.HasPrefix(dec.text, "tst") && raw&0x0ff00000 != 0x03100000 && raw&0x0ff00000 != 0x01100000 { + return true + } + + // word C3203CE8: libopcodes says teq, but no evidence for that. + if strings.HasPrefix(dec.text, "teq") && raw&0x0ff00000 != 0x03300000 && raw&0x0ff00000 != 0x01300000 { + return true + } + + // word D14C552E: libopcodes says cmp but no evidence for that. + if strings.HasPrefix(dec.text, "cmp") && raw&0x0ff00000 != 0x03500000 && raw&0x0ff00000 != 0x01500000 { + return true + } + + // word 2166AA4A: libopcodes says cmn but no evidence for that. + if strings.HasPrefix(dec.text, "cmn") && raw&0x0ff00000 != 0x03700000 && raw&0x0ff00000 != 0x01700000 { + return true + } + + // word E70AEEEF: libopcodes says str but no evidence for that. + if strings.HasPrefix(dec.text, "str") && len(dec.text) >= 5 && (dec.text[3] == ' ' || dec.text[5] == ' ') && raw&0x0e500018 != 0x06000000 && raw&0x0e500000 != 0x0400000 { + return true + } + + // word B0AF48F4: libopcodes says strd but P=0,W=1 which is unpredictable. + if hasPrefix(dec.text, "ldr", "str") && raw&0x01200000 == 0x00200000 { + return true + } + + // word B6CC1C76: libopcodes inexplicably says 'uxtab16lt r1, ip, r6, ROR #24' instead of 'uxtab16lt r1, ip, r6, ror #24' + if strings.ToLower(dec.text) == text { + return true + } + + // word F410FDA1: libopcodes says PLDW but the manual is clear that PLDW is F5/F7, not F4. + // word F7D0FB17: libopcodes says PLDW but the manual is clear that PLDW has 0x10 clear + if hasPrefix(dec.text, "pld") && raw&0xfd000010 != 0xf5000000 { + return true + } + + // word F650FE14: libopcodes says PLI but the manual is clear that PLI has 0x10 clear + if hasPrefix(dec.text, "pli") && raw&0xff000010 != 0xf6000000 { + return true + } + } + + return false +} + +// Instructions known to libopcodes (or xed) but not to us. +// Most of these are floating point coprocessor instructions. +var unsupported = strings.Fields(` + abs + acs + adf + aes + asn + atn + cdp + cf + cmf + cnf + cos + cps + crc32 + dvf + eret + exp + fadd + fcmp + fcpy + fcvt + fdiv + fdv + fix + fld + flt + fmac + fmd + fml + fmr + fms + fmul + fmx + fneg + fnm + frd + fsit + fsq + fst + fsu + fto + fui + hlt + hvc + lda + ldc + ldf + lfm + lgn + log + mar + mcr + mcrr + mia + mnf + mra + mrc + mrrc + mrs + msr + msr + muf + mvf + nrm + pol + pow + rdf + rfc + rfe + rfs + rmf + rnd + rpw + rsf + sdiv + sev + sfm + sha1 + sha256 + sin + smc + sqt + srs + stc + stf + stl + suf + tan + udf + udiv + urd + vfma + vfms + vfnma + vfnms + vrint + wfc + wfs +`) diff --git a/src/cmd/internal/rsc.io/arm/armasm/objdumpext_test.go b/src/cmd/internal/rsc.io/arm/armasm/objdumpext_test.go new file mode 100644 index 0000000000..d88c67fc05 --- /dev/null +++ b/src/cmd/internal/rsc.io/arm/armasm/objdumpext_test.go @@ -0,0 +1,260 @@ +// Copyright 2014 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. + +// Copied and simplified from rsc.io/x86/x86asm/objdumpext_test.go. + +package armasm + +import ( + "bytes" + "debug/elf" + "encoding/binary" + "fmt" + "io" + "log" + "os" + "strconv" + "strings" + "testing" +) + +const objdumpPath = "/usr/local/bin/arm-linux-elf-objdump" + +func testObjdumpARM(t *testing.T, generate func(func([]byte))) { + testObjdumpArch(t, generate, ModeARM) +} + +func testObjdumpArch(t *testing.T, generate func(func([]byte)), arch Mode) { + if testing.Short() { + t.Skip("skipping objdump test in short mode") + } + + if _, err := os.Stat(objdumpPath); err != nil { + t.Fatal(err) + } + + testExtDis(t, "gnu", arch, objdump, generate, allowedMismatchObjdump) +} + +func objdump(ext *ExtDis) error { + // File already written with instructions; add ELF header. + if ext.Arch == ModeARM { + if err := writeELF32(ext.File, ext.Size); err != nil { + return err + } + } else { + panic("unknown arch") + } + + b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name()) + if err != nil { + return err + } + + var ( + nmatch int + reading bool + next uint32 = start + addr uint32 + encbuf [4]byte + enc []byte + text string + ) + flush := func() { + if addr == next { + if m := pcrel.FindStringSubmatch(text); m != nil { + targ, _ := strconv.ParseUint(m[2], 16, 64) + text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc)))) + } + if strings.HasPrefix(text, "stmia") { + text = "stm" + text[5:] + } + if strings.HasPrefix(text, "stmfd") { + text = "stmdb" + text[5:] + } + if strings.HasPrefix(text, "ldmfd") { + text = "ldm" + text[5:] + } + text = strings.Replace(text, "#0.0", "#0", -1) + if text == "undefined" && len(enc) == 4 { + text = "error: unknown instruction" + enc = nil + } + if len(enc) == 4 { + // prints as word but we want to record bytes + enc[0], enc[3] = enc[3], enc[0] + enc[1], enc[2] = enc[2], enc[1] + } + ext.Dec <- ExtInst{addr, encbuf, len(enc), text} + encbuf = [4]byte{} + enc = nil + next += 4 + } + } + var textangle = []byte("<.text>:") + for { + line, err := b.ReadSlice('\n') + if err != nil { + if err == io.EOF { + break + } + return fmt.Errorf("reading objdump output: %v", err) + } + if bytes.Contains(line, textangle) { + reading = true + continue + } + if !reading { + continue + } + if debug { + os.Stdout.Write(line) + } + if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil { + enc = enc1 + continue + } + flush() + nmatch++ + addr, enc, text = parseLine(line, encbuf[:0]) + if addr > next { + return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line) + } + } + flush() + if next != start+uint32(ext.Size) { + return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size) + } + if err := ext.Wait(); err != nil { + return fmt.Errorf("exec: %v", err) + } + + return nil +} + +var ( + undefined = []byte("") + unpredictable = []byte("") + illegalShifter = []byte("") +) + +func parseLine(line []byte, encstart []byte) (addr uint32, enc []byte, text string) { + oline := line + i := index(line, ":\t") + if i < 0 { + log.Fatalf("cannot parse disassembly: %q", oline) + } + x, err := strconv.ParseUint(string(trimSpace(line[:i])), 16, 32) + if err != nil { + log.Fatalf("cannot parse disassembly: %q", oline) + } + addr = uint32(x) + line = line[i+2:] + i = bytes.IndexByte(line, '\t') + if i < 0 { + log.Fatalf("cannot parse disassembly: %q", oline) + } + enc, ok := parseHex(line[:i], encstart) + if !ok { + log.Fatalf("cannot parse disassembly: %q", oline) + } + line = trimSpace(line[i:]) + if bytes.Contains(line, undefined) { + text = "undefined" + return + } + if bytes.Contains(line, illegalShifter) { + text = "undefined" + return + } + if false && bytes.Contains(line, unpredictable) { + text = "unpredictable" + return + } + if i := bytes.IndexByte(line, ';'); i >= 0 { + line = trimSpace(line[:i]) + } + text = string(fixSpace(line)) + return +} + +func parseContinuation(line []byte, enc []byte) []byte { + i := index(line, ":\t") + if i < 0 { + return nil + } + line = line[i+1:] + enc, _ = parseHex(line, enc) + return enc +} + +// writeELF32 writes an ELF32 header to the file, +// describing a text segment that starts at start +// and extends for size bytes. +func writeELF32(f *os.File, size int) error { + f.Seek(0, 0) + var hdr elf.Header32 + var prog elf.Prog32 + var sect elf.Section32 + var buf bytes.Buffer + binary.Write(&buf, binary.LittleEndian, &hdr) + off1 := buf.Len() + binary.Write(&buf, binary.LittleEndian, &prog) + off2 := buf.Len() + binary.Write(&buf, binary.LittleEndian, §) + off3 := buf.Len() + buf.Reset() + data := byte(elf.ELFDATA2LSB) + hdr = elf.Header32{ + Ident: [16]byte{0x7F, 'E', 'L', 'F', 1, data, 1}, + Type: 2, + Machine: uint16(elf.EM_ARM), + Version: 1, + Entry: start, + Phoff: uint32(off1), + Shoff: uint32(off2), + Flags: 0x05000002, + Ehsize: uint16(off1), + Phentsize: uint16(off2 - off1), + Phnum: 1, + Shentsize: uint16(off3 - off2), + Shnum: 3, + Shstrndx: 2, + } + binary.Write(&buf, binary.LittleEndian, &hdr) + prog = elf.Prog32{ + Type: 1, + Off: start, + Vaddr: start, + Paddr: start, + Filesz: uint32(size), + Memsz: uint32(size), + Flags: 5, + Align: start, + } + binary.Write(&buf, binary.LittleEndian, &prog) + binary.Write(&buf, binary.LittleEndian, §) // NULL section + sect = elf.Section32{ + Name: 1, + Type: uint32(elf.SHT_PROGBITS), + Addr: start, + Off: start, + Size: uint32(size), + Flags: uint32(elf.SHF_ALLOC | elf.SHF_EXECINSTR), + Addralign: 4, + } + binary.Write(&buf, binary.LittleEndian, §) // .text + sect = elf.Section32{ + Name: uint32(len("\x00.text\x00")), + Type: uint32(elf.SHT_STRTAB), + Addr: 0, + Off: uint32(off2 + (off3-off2)*3), + Size: uint32(len("\x00.text\x00.shstrtab\x00")), + Addralign: 1, + } + binary.Write(&buf, binary.LittleEndian, §) + buf.WriteString("\x00.text\x00.shstrtab\x00") + f.Write(buf.Bytes()) + return nil +} diff --git a/src/cmd/internal/rsc.io/arm/armasm/plan9x.go b/src/cmd/internal/rsc.io/arm/armasm/plan9x.go new file mode 100644 index 0000000000..952c5190b6 --- /dev/null +++ b/src/cmd/internal/rsc.io/arm/armasm/plan9x.go @@ -0,0 +1,211 @@ +// Copyright 2014 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 armasm + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "strings" +) + +// Plan9Syntax returns the Go assembler syntax for the instruction. +// The syntax was originally defined by Plan 9. +// The pc is the program counter of the instruction, used for expanding +// PC-relative addresses into absolute ones. +// The symname function queries the symbol table for the program +// being disassembled. Given a target address it returns the name and base +// address of the symbol containing the target, if any; otherwise it returns "", 0. +// The reader r should read from the text segment using text addresses +// as offsets; it is used to display pc-relative loads as constant loads. +func Plan9Syntax(inst Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string { + if symname == nil { + symname = func(uint64) (string, uint64) { return "", 0 } + } + + var args []string + for _, a := range inst.Args { + if a == nil { + break + } + args = append(args, plan9Arg(&inst, pc, symname, a)) + } + + op := inst.Op.String() + + switch inst.Op &^ 15 { + case LDR_EQ, LDRB_EQ, LDRH_EQ: + // Check for RET + reg, _ := inst.Args[0].(Reg) + mem, _ := inst.Args[1].(Mem) + if inst.Op&^15 == LDR_EQ && reg == R15 && mem.Base == SP && mem.Sign == 0 && mem.Mode == AddrPostIndex { + return fmt.Sprintf("RET%s #%d", op[3:], mem.Offset) + } + + // Check for PC-relative load. + if mem.Base == PC && mem.Sign == 0 && mem.Mode == AddrOffset && text != nil { + addr := uint32(pc) + 8 + uint32(mem.Offset) + buf := make([]byte, 4) + switch inst.Op &^ 15 { + case LDRB_EQ: + if _, err := text.ReadAt(buf[:1], int64(addr)); err != nil { + break + } + args[1] = fmt.Sprintf("$%#x", buf[0]) + + case LDRH_EQ: + if _, err := text.ReadAt(buf[:2], int64(addr)); err != nil { + break + } + args[1] = fmt.Sprintf("$%#x", binary.LittleEndian.Uint16(buf)) + + case LDR_EQ: + if _, err := text.ReadAt(buf, int64(addr)); err != nil { + break + } + x := binary.LittleEndian.Uint32(buf) + if s, base := symname(uint64(x)); s != "" && uint64(x) == base { + args[1] = fmt.Sprintf("$%s(SB)", s) + } else { + args[1] = fmt.Sprintf("$%#x", x) + } + } + } + } + + // Move addressing mode into opcode suffix. + suffix := "" + switch inst.Op &^ 15 { + case LDR_EQ, LDRB_EQ, LDRH_EQ, STR_EQ, STRB_EQ, STRH_EQ: + mem, _ := inst.Args[1].(Mem) + switch mem.Mode { + case AddrOffset, AddrLDM: + // no suffix + case AddrPreIndex, AddrLDM_WB: + suffix = ".W" + case AddrPostIndex: + suffix = ".P" + } + off := "" + if mem.Offset != 0 { + off = fmt.Sprintf("%#x", mem.Offset) + } + base := fmt.Sprintf("(R%d)", int(mem.Base)) + index := "" + if mem.Sign != 0 { + sign := "" + if mem.Sign < 0 { + sign = "" + } + shift := "" + if mem.Count != 0 { + shift = fmt.Sprintf("%s%d", plan9Shift[mem.Shift], mem.Count) + } + index = fmt.Sprintf("(%sR%d%s)", sign, int(mem.Index), shift) + } + args[1] = off + base + index + } + + // Reverse args, placing dest last. + for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 { + args[i], args[j] = args[j], args[i] + } + + switch inst.Op &^ 15 { + case MOV_EQ: + op = "MOVW" + op[3:] + + case LDR_EQ: + op = "MOVW" + op[3:] + suffix + case LDRB_EQ: + op = "MOVB" + op[4:] + suffix + case LDRH_EQ: + op = "MOVH" + op[4:] + suffix + + case STR_EQ: + op = "MOVW" + op[3:] + suffix + args[0], args[1] = args[1], args[0] + case STRB_EQ: + op = "MOVB" + op[4:] + suffix + args[0], args[1] = args[1], args[0] + case STRH_EQ: + op = "MOVH" + op[4:] + suffix + args[0], args[1] = args[1], args[0] + } + + if args != nil { + op += " " + strings.Join(args, ", ") + } + + return op +} + +// assembler syntax for the various shifts. +// @x> is a lie; the assembler uses @> 0 +// instead of @x> 1, but i wanted to be clear that it +// was a different operation (rotate right extended, not rotate right). +var plan9Shift = []string{"<<", ">>", "->", "@>", "@x>"} + +func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string { + switch a := arg.(type) { + case Endian: + + case Imm: + return fmt.Sprintf("$%d", int(a)) + + case Mem: + + case PCRel: + addr := uint32(pc) + 8 + uint32(a) + if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base { + return fmt.Sprintf("%s(SB)", s) + } + return fmt.Sprintf("%#x", addr) + + case Reg: + if a < 16 { + return fmt.Sprintf("R%d", int(a)) + } + + case RegList: + var buf bytes.Buffer + start := -2 + end := -2 + fmt.Fprintf(&buf, "[") + flush := func() { + if start >= 0 { + if buf.Len() > 1 { + fmt.Fprintf(&buf, ",") + } + if start == end { + fmt.Fprintf(&buf, "R%d", start) + } else { + fmt.Fprintf(&buf, "R%d-R%d", start, end) + } + } + } + for i := 0; i < 16; i++ { + if a&(1< ,,# cond:4|0|0|1|0|1|0|1|S|Rn:4|Rd:4|imm12:12 + {0x0fe00090, 0x00a00010, 4, ADC_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_R}}, // ADC{S} ,,, cond:4|0|0|0|0|1|0|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4 + {0x0fe00010, 0x00a00000, 2, ADC_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_imm}}, // ADC{S} ,,{,} cond:4|0|0|0|0|1|0|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0fe00000, 0x02800000, 2, ADD_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_const}}, // ADD{S} ,,# cond:4|0|0|1|0|1|0|0|S|Rn:4|Rd:4|imm12:12 + {0x0fe00090, 0x00800010, 4, ADD_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_R}}, // ADD{S} ,,, cond:4|0|0|0|0|1|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4 + {0x0fe00010, 0x00800000, 2, ADD_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_imm}}, // ADD{S} ,,{,} cond:4|0|0|0|0|1|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0fef0000, 0x028d0000, 2, ADD_EQ, 0x14011c04, instArgs{arg_R_12, arg_SP, arg_const}}, // ADD{S} ,SP,# cond:4|0|0|1|0|1|0|0|S|1|1|0|1|Rd:4|imm12:12 + {0x0fef0010, 0x008d0000, 2, ADD_EQ, 0x14011c04, instArgs{arg_R_12, arg_SP, arg_R_shift_imm}}, // ADD{S} ,SP,{,} cond:4|0|0|0|0|1|0|0|S|1|1|0|1|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0fe00000, 0x02000000, 2, AND_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_const}}, // AND{S} ,,# cond:4|0|0|1|0|0|0|0|S|Rn:4|Rd:4|imm12:12 + {0x0fe00090, 0x00000010, 4, AND_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_R}}, // AND{S} ,,, cond:4|0|0|0|0|0|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4 + {0x0fe00010, 0x00000000, 2, AND_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_imm}}, // AND{S} ,,{,} cond:4|0|0|0|0|0|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0fef0070, 0x01a00040, 4, ASR_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_0, arg_imm5_32}}, // ASR{S} ,,# cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|1|0|0|Rm:4 + {0x0fef00f0, 0x01a00050, 4, ASR_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_0, arg_R_8}}, // ASR{S} ,, cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|1|0|1|Rn:4 + {0x0f000000, 0x0a000000, 4, B_EQ, 0x1c04, instArgs{arg_label24}}, // B cond:4|1|0|1|0|imm24:24 + {0x0fe0007f, 0x07c0001f, 4, BFC_EQ, 0x1c04, instArgs{arg_R_12, arg_imm5, arg_lsb_width}}, // BFC ,#,# cond:4|0|1|1|1|1|1|0|msb:5|Rd:4|lsb:5|0|0|1|1|1|1|1 + {0x0fe00070, 0x07c00010, 2, BFI_EQ, 0x1c04, instArgs{arg_R_12, arg_R_0, arg_imm5, arg_lsb_width}}, // BFI ,,#,# cond:4|0|1|1|1|1|1|0|msb:5|Rd:4|lsb:5|0|0|1|Rn:4 + {0x0fe00000, 0x03c00000, 2, BIC_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_const}}, // BIC{S} ,,# cond:4|0|0|1|1|1|1|0|S|Rn:4|Rd:4|imm12:12 + {0x0fe00090, 0x01c00010, 4, BIC_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_R}}, // BIC{S} ,,, cond:4|0|0|0|1|1|1|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4 + {0x0fe00010, 0x01c00000, 2, BIC_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_imm}}, // BIC{S} ,,{,} cond:4|0|0|0|1|1|1|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0ff000f0, 0x01200070, 4, BKPT_EQ, 0x1c04, instArgs{arg_imm_12at8_4at0}}, // BKPT # cond:4|0|0|0|1|0|0|1|0|imm12:12|0|1|1|1|imm4:4 + {0x0f000000, 0x0b000000, 4, BL_EQ, 0x1c04, instArgs{arg_label24}}, // BL cond:4|1|0|1|1|imm24:24 + {0xfe000000, 0xfa000000, 4, BLX, 0x0, instArgs{arg_label24H}}, // BLX 1|1|1|1|1|0|1|H|imm24:24 + {0x0ffffff0, 0x012fff30, 4, BLX_EQ, 0x1c04, instArgs{arg_R_0}}, // BLX cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4 + {0x0ff000f0, 0x012fff30, 3, BLX_EQ, 0x1c04, instArgs{arg_R_0}}, // BLX cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4 + {0x0ffffff0, 0x012fff10, 4, BX_EQ, 0x1c04, instArgs{arg_R_0}}, // BX cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4 + {0x0ff000f0, 0x012fff10, 3, BX_EQ, 0x1c04, instArgs{arg_R_0}}, // BX cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4 + {0x0ffffff0, 0x012fff20, 4, BXJ_EQ, 0x1c04, instArgs{arg_R_0}}, // BXJ cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|0|Rm:4 + {0x0ff000f0, 0x012fff20, 3, BXJ_EQ, 0x1c04, instArgs{arg_R_0}}, // BXJ cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|0|Rm:4 + {0xffffffff, 0xf57ff01f, 4, CLREX, 0x0, instArgs{}}, // CLREX 1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|1|(1)|(1)|(1)|(1) + {0xfff000f0, 0xf57ff01f, 3, CLREX, 0x0, instArgs{}}, // CLREX 1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|1|(1)|(1)|(1)|(1) + {0x0fff0ff0, 0x016f0f10, 4, CLZ_EQ, 0x1c04, instArgs{arg_R_12, arg_R_0}}, // CLZ , cond:4|0|0|0|1|0|1|1|0|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4 + {0x0ff000f0, 0x016f0f10, 3, CLZ_EQ, 0x1c04, instArgs{arg_R_12, arg_R_0}}, // CLZ , cond:4|0|0|0|1|0|1|1|0|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4 + {0x0ff0f000, 0x03700000, 4, CMN_EQ, 0x1c04, instArgs{arg_R_16, arg_const}}, // CMN ,# cond:4|0|0|1|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12 + {0x0ff00000, 0x03700000, 3, CMN_EQ, 0x1c04, instArgs{arg_R_16, arg_const}}, // CMN ,# cond:4|0|0|1|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12 + {0x0ff0f090, 0x01700010, 4, CMN_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_R}}, // CMN ,, cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4 + {0x0ff00090, 0x01700010, 3, CMN_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_R}}, // CMN ,, cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4 + {0x0ff0f010, 0x01700000, 4, CMN_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_imm}}, // CMN ,{,} cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4 + {0x0ff00010, 0x01700000, 3, CMN_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_imm}}, // CMN ,{,} cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4 + {0x0ff0f000, 0x03500000, 4, CMP_EQ, 0x1c04, instArgs{arg_R_16, arg_const}}, // CMP ,# cond:4|0|0|1|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12 + {0x0ff00000, 0x03500000, 3, CMP_EQ, 0x1c04, instArgs{arg_R_16, arg_const}}, // CMP ,# cond:4|0|0|1|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12 + {0x0ff0f090, 0x01500010, 4, CMP_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_R}}, // CMP ,, cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4 + {0x0ff00090, 0x01500010, 3, CMP_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_R}}, // CMP ,, cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4 + {0x0ff0f010, 0x01500000, 4, CMP_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_imm}}, // CMP ,{,} cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4 + {0x0ff00010, 0x01500000, 3, CMP_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_imm}}, // CMP ,{,} cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4 + {0x0ffffff0, 0x0320f0f0, 4, DBG_EQ, 0x1c04, instArgs{arg_option}}, // DBG #