summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Stapelberg <michael@stapelberg.de>2013-04-05 18:22:52 +0200
committerMichael Stapelberg <michael@stapelberg.de>2013-04-05 18:22:52 +0200
commit35af5d98b00263f6d0723166e1c09b7b67ad0fe0 (patch)
tree508f228f500a027d4529bca88bce4ba277725f05 /src
parentc06e14c05733f526ce0a6ed799abcfafb2d1437b (diff)
parent6e7a55a2c0ab461fd1c8e68c3c7366b0afa4d4d4 (diff)
downloadgolang-35af5d98b00263f6d0723166e1c09b7b67ad0fe0.tar.gz
Merge tag 'upstream/1.1_hg20130405' into debian-sid
Upstream version 1.1~hg20130405
Diffstat (limited to 'src')
-rw-r--r--src/all.bat2
-rw-r--r--src/cmd/5l/5.out.h1
-rw-r--r--src/cmd/5l/l.h1
-rw-r--r--src/cmd/5l/obj.c15
-rw-r--r--src/cmd/6g/peep.c5
-rw-r--r--src/cmd/6g/reg.c5
-rw-r--r--src/cmd/6l/6.out.h1
-rw-r--r--src/cmd/6l/asm.c7
-rw-r--r--src/cmd/6l/l.h1
-rw-r--r--src/cmd/6l/obj.c15
-rw-r--r--src/cmd/6l/span.c24
-rw-r--r--src/cmd/8a/a.y9
-rw-r--r--src/cmd/8a/lex.c2
-rw-r--r--src/cmd/8a/y.tab.c1326
-rw-r--r--src/cmd/8a/y.tab.h46
-rw-r--r--src/cmd/8l/8.out.h3
-rw-r--r--src/cmd/8l/asm.c6
-rw-r--r--src/cmd/8l/l.h3
-rw-r--r--src/cmd/8l/obj.c15
-rw-r--r--src/cmd/8l/optab.c8
-rw-r--r--src/cmd/8l/pass.c50
-rw-r--r--src/cmd/8l/span.c35
-rw-r--r--src/cmd/cc/cc.h1
-rw-r--r--src/cmd/cc/funct.c2
-rw-r--r--src/cmd/cc/lexbody2
-rw-r--r--src/cmd/dist/a.h1
-rw-r--r--src/cmd/dist/build.c10
-rw-r--r--src/cmd/dist/buildruntime.c11
-rw-r--r--src/cmd/dist/unix.c13
-rw-r--r--src/cmd/gc/builtin.c1
-rw-r--r--src/cmd/gc/closure.c6
-rw-r--r--src/cmd/gc/const.c1
-rw-r--r--src/cmd/gc/dcl.c8
-rw-r--r--src/cmd/gc/fmt.c2
-rw-r--r--src/cmd/gc/go.h3
-rw-r--r--src/cmd/gc/go.y3
-rw-r--r--src/cmd/gc/lex.c10
-rwxr-xr-xsrc/cmd/gc/mkbuiltin1
-rw-r--r--src/cmd/gc/racewalk.c100
-rw-r--r--src/cmd/gc/range.c4
-rw-r--r--src/cmd/gc/reflect.c7
-rw-r--r--src/cmd/gc/runtime.go6
-rw-r--r--src/cmd/gc/subr.c28
-rw-r--r--src/cmd/gc/walk.c49
-rw-r--r--src/cmd/gc/y.tab.c589
-rw-r--r--src/cmd/go/clean.go2
-rw-r--r--src/cmd/go/doc.go19
-rw-r--r--src/cmd/go/env.go1
-rwxr-xr-xsrc/cmd/go/mkdoc.sh1
-rw-r--r--src/cmd/go/run.go17
-rw-r--r--src/cmd/go/test.go16
-rw-r--r--src/cmd/godoc/doc.go2
-rw-r--r--src/cmd/godoc/format.go80
-rw-r--r--src/cmd/godoc/godoc.go98
-rw-r--r--src/cmd/godoc/index.go2
-rw-r--r--src/cmd/godoc/linkify.go234
-rw-r--r--src/cmd/godoc/main.go16
-rw-r--r--src/cmd/godoc/utils.go2
-rw-r--r--src/cmd/gofmt/gofmt_test.go3
-rw-r--r--src/cmd/gofmt/rewrite.go16
-rw-r--r--src/cmd/gofmt/testdata/rewrite6.golden15
-rw-r--r--src/cmd/gofmt/testdata/rewrite6.input15
-rw-r--r--src/cmd/gofmt/testdata/rewrite7.golden15
-rw-r--r--src/cmd/gofmt/testdata/rewrite7.input15
-rw-r--r--src/cmd/gofmt/testdata/rewrite8.golden10
-rw-r--r--src/cmd/gofmt/testdata/rewrite8.input10
-rw-r--r--src/cmd/ld/data.c34
-rw-r--r--src/cmd/ld/doc.go17
-rw-r--r--src/cmd/ld/dwarf.c72
-rw-r--r--src/cmd/ld/elf.c8
-rw-r--r--src/cmd/ld/go.c8
-rw-r--r--src/cmd/ld/lib.c50
-rw-r--r--src/cmd/ld/lib.h10
-rw-r--r--src/cmd/ld/symtab.c20
-rw-r--r--src/cmd/nm/nm.c6
-rw-r--r--src/cmd/vet/asmdecl.go1
-rw-r--r--src/cmd/vet/test_asm1.s4
-rw-r--r--src/cmd/vet/test_asm2.s4
-rw-r--r--src/cmd/vet/test_asm3.s4
-rw-r--r--src/cmd/vet/test_deadcode.go144
-rw-r--r--src/lib9/goos.c6
-rw-r--r--src/lib9/utf/utf.h2
-rw-r--r--src/libmach/sym.c7
-rwxr-xr-xsrc/make.bash5
-rw-r--r--src/make.bat4
-rw-r--r--src/pkg/bufio/scan.go5
-rw-r--r--src/pkg/builtin/builtin.go9
-rw-r--r--src/pkg/bytes/asm_386.s16
-rw-r--r--src/pkg/bytes/asm_amd64.s17
-rw-r--r--src/pkg/bytes/buffer.go56
-rw-r--r--src/pkg/bytes/buffer_test.go50
-rw-r--r--src/pkg/bytes/bytes_decl.go2
-rw-r--r--src/pkg/bytes/bytes_test.go76
-rw-r--r--src/pkg/bytes/equal_test.go47
-rw-r--r--src/pkg/bytes/export_test.go4
-rw-r--r--src/pkg/compress/gzip/gzip.go24
-rw-r--r--src/pkg/compress/gzip/gzip_test.go40
-rw-r--r--src/pkg/crypto/rsa/rsa.go4
-rw-r--r--src/pkg/crypto/rsa/rsa_test.go3
-rw-r--r--src/pkg/database/sql/convert.go22
-rw-r--r--src/pkg/database/sql/convert_test.go3
-rw-r--r--src/pkg/database/sql/fakedb_test.go21
-rw-r--r--src/pkg/database/sql/sql.go157
-rw-r--r--src/pkg/database/sql/sql_test.go129
-rw-r--r--src/pkg/go/build/build.go8
-rw-r--r--src/pkg/go/doc/doc.go8
-rw-r--r--src/pkg/go/doc/reader.go1
-rw-r--r--src/pkg/go/parser/interface.go29
-rw-r--r--src/pkg/go/parser/parser_test.go13
-rw-r--r--src/pkg/go/printer/printer.go31
-rw-r--r--src/pkg/image/gif/reader_test.go17
-rw-r--r--src/pkg/math/big/int.go12
-rw-r--r--src/pkg/math/big/rat.go14
-rw-r--r--src/pkg/math/big/rat_test.go10
-rw-r--r--src/pkg/math/hypot.go6
-rw-r--r--src/pkg/math/tanh.go2
-rw-r--r--src/pkg/net/dial.go237
-rw-r--r--src/pkg/net/dial_gen.go61
-rw-r--r--src/pkg/net/example_test.go1
-rw-r--r--src/pkg/net/fd_plan9.go4
-rw-r--r--src/pkg/net/fd_unix.go5
-rw-r--r--src/pkg/net/fd_windows.go15
-rw-r--r--src/pkg/net/file_windows.go12
-rw-r--r--src/pkg/net/http/example_test.go13
-rw-r--r--src/pkg/net/http/request.go42
-rw-r--r--src/pkg/net/http/response_test.go6
-rw-r--r--src/pkg/net/http/serve_test.go389
-rw-r--r--src/pkg/net/http/server.go227
-rw-r--r--src/pkg/net/http/transfer.go47
-rw-r--r--src/pkg/net/http/transport.go15
-rw-r--r--src/pkg/net/http/transport_test.go129
-rw-r--r--src/pkg/net/http/z_last_test.go14
-rw-r--r--src/pkg/net/interface_unix_test.go10
-rw-r--r--src/pkg/net/ipraw_test.go33
-rw-r--r--src/pkg/net/iprawsock.go2
-rw-r--r--src/pkg/net/iprawsock_plan9.go4
-rw-r--r--src/pkg/net/iprawsock_posix.go30
-rw-r--r--src/pkg/net/multicast_test.go2
-rw-r--r--src/pkg/net/server_test.go2
-rw-r--r--src/pkg/net/smtp/auth.go11
-rw-r--r--src/pkg/net/smtp/smtp_test.go35
-rw-r--r--src/pkg/net/sock_posix.go13
-rw-r--r--src/pkg/net/tcp_test.go42
-rw-r--r--src/pkg/net/tcpsock.go2
-rw-r--r--src/pkg/net/tcpsock_plan9.go6
-rw-r--r--src/pkg/net/tcpsock_posix.go61
-rw-r--r--src/pkg/net/textproto/reader.go1
-rw-r--r--src/pkg/net/textproto/reader_test.go2
-rw-r--r--src/pkg/net/udp_test.go80
-rw-r--r--src/pkg/net/udpsock.go2
-rw-r--r--src/pkg/net/udpsock_plan9.go15
-rw-r--r--src/pkg/net/udpsock_posix.go43
-rw-r--r--src/pkg/net/unicast_posix_test.go65
-rw-r--r--src/pkg/net/unix_test.go132
-rw-r--r--src/pkg/net/unixsock.go2
-rw-r--r--src/pkg/net/unixsock_plan9.go8
-rw-r--r--src/pkg/net/unixsock_posix.go33
-rw-r--r--src/pkg/net/url/url.go25
-rw-r--r--src/pkg/os/exec/exec_test.go2
-rw-r--r--src/pkg/reflect/all_test.go183
-rw-r--r--src/pkg/reflect/type.go173
-rw-r--r--src/pkg/runtime/alg.c23
-rw-r--r--src/pkg/runtime/asm_386.s128
-rw-r--r--src/pkg/runtime/asm_amd64.s120
-rw-r--r--src/pkg/runtime/asm_arm.s45
-rw-r--r--src/pkg/runtime/cgo/gcc_freebsd_386.c14
-rw-r--r--src/pkg/runtime/cgo/gcc_freebsd_amd64.c16
-rw-r--r--src/pkg/runtime/cgo/gcc_linux_386.c17
-rw-r--r--src/pkg/runtime/cgo/gcc_linux_amd64.c15
-rw-r--r--src/pkg/runtime/cgo/gcc_netbsd_386.c14
-rw-r--r--src/pkg/runtime/cgo/gcc_netbsd_amd64.c15
-rw-r--r--src/pkg/runtime/cgo/gcc_openbsd_386.c14
-rw-r--r--src/pkg/runtime/cgo/gcc_openbsd_amd64.c15
-rw-r--r--src/pkg/runtime/cgocall.c15
-rw-r--r--src/pkg/runtime/gc_test.go2
-rw-r--r--src/pkg/runtime/hashmap.c137
-rw-r--r--src/pkg/runtime/hashmap_fast.c104
-rw-r--r--src/pkg/runtime/malloc.goc9
-rw-r--r--src/pkg/runtime/map_test.go72
-rw-r--r--src/pkg/runtime/mapspeed_test.go58
-rw-r--r--src/pkg/runtime/mgc0.c94
-rw-r--r--src/pkg/runtime/mgc0.h3
-rwxr-xr-xsrc/pkg/runtime/mkasmh.sh138
-rw-r--r--src/pkg/runtime/mprof.goc2
-rw-r--r--src/pkg/runtime/netpoll_epoll.c7
-rw-r--r--src/pkg/runtime/netpoll_kqueue.c7
-rw-r--r--src/pkg/runtime/proc.c2
-rw-r--r--src/pkg/runtime/race.c7
-rw-r--r--src/pkg/runtime/race/testdata/mop_test.go148
-rw-r--r--src/pkg/runtime/race/testdata/slice_test.go3
-rw-r--r--src/pkg/runtime/runtime-gdb.py49
-rw-r--r--src/pkg/runtime/runtime.h12
-rw-r--r--src/pkg/runtime/string.goc10
-rw-r--r--src/pkg/runtime/string_test.go28
-rw-r--r--src/pkg/runtime/traceback_arm.c34
-rw-r--r--src/pkg/runtime/traceback_x86.c37
-rw-r--r--src/pkg/sort/example_multi_test.go132
-rw-r--r--src/pkg/strconv/strconv_test.go6
-rw-r--r--src/pkg/sync/atomic/asm_arm.s16
-rw-r--r--src/pkg/sync/atomic/asm_linux_arm.s8
-rw-r--r--src/pkg/sync/atomic/atomic_test.go6
-rw-r--r--src/pkg/syscall/passfd_test.go2
-rw-r--r--src/pkg/testing/testing.go11
-rw-r--r--src/pkg/text/template/exec.go2
-rw-r--r--src/pkg/text/template/exec_test.go27
-rwxr-xr-xsrc/run.bash27
-rw-r--r--src/run.bat6
207 files changed, 5505 insertions, 2657 deletions
diff --git a/src/all.bat b/src/all.bat
index 8929f7727..0647a715b 100644
--- a/src/all.bat
+++ b/src/all.bat
@@ -20,7 +20,7 @@ if %GOBUILDFAIL%==1 goto end
:: can get the original %PATH% and give suggestion to add %GOROOT%/bin
:: to %PATH% if necessary.
set PATH=%OLDPATH%
-%GOTOOLDIR%/dist banner
+"%GOTOOLDIR%/dist" banner
:end
if x%GOBUILDEXIT%==x1 exit %GOBUILDFAIL%
diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h
index 4aef8a27f..97a2421b3 100644
--- a/src/cmd/5l/5.out.h
+++ b/src/cmd/5l/5.out.h
@@ -275,6 +275,7 @@ enum as
#define D_PLT1 (D_NONE+44) // R_ARM_PLT32, 2nd inst: add ip, ip, #0xNN000
#define D_PLT2 (D_NONE+45) // R_ARM_PLT32, 3rd inst: ldr pc, [ip, #0xNNN]!
#define D_CALL (D_NONE+46) // R_ARM_PLT32/R_ARM_CALL/R_ARM_JUMP24, bl xxxxx or b yyyyy
+#define D_TLS (D_NONE+47)
/*
* this is the ranlib header
diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h
index 83c8b755c..e7794c723 100644
--- a/src/cmd/5l/l.h
+++ b/src/cmd/5l/l.h
@@ -299,7 +299,6 @@ EXTERN Auto* curhist;
EXTERN Prog* curp;
EXTERN Sym* cursym;
EXTERN Sym* datap;
-EXTERN int32 elfdatsize;
EXTERN int debug[128];
EXTERN Sym* etextp;
EXTERN char* noname;
diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c
index 1bcf436c4..24e6294a8 100644
--- a/src/cmd/5l/obj.c
+++ b/src/cmd/5l/obj.c
@@ -114,6 +114,8 @@ main(int argc, char *argv[])
flagcount("a", "disassemble output", &debug['a']);
flagcount("c", "dump call graph", &debug['c']);
flagcount("d", "disable dynamic executable", &debug['d']);
+ flagstr("extld", "linker to run in external mode", &extld);
+ flagstr("extldflags", "flags for external linker", &extldflags);
flagcount("f", "ignore version mismatch", &debug['f']);
flagcount("g", "disable go package data checks", &debug['g']);
flagstr("k", "sym: set field tracking symbol", &tracksym);
@@ -136,9 +138,16 @@ main(int argc, char *argv[])
if(argc != 1)
usage();
- if(linkmode != LinkInternal) {
+ // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when
+ // Go was built; see ../../make.bash.
+ if(linkmode == LinkAuto && strcmp(getgoextlinkenabled(), "0") == 0)
+ linkmode = LinkInternal;
+
+ if(linkmode == LinkExternal) {
diag("only -linkmode=internal is supported");
errorexit();
+ } else if(linkmode == LinkAuto) {
+ linkmode = LinkInternal;
}
libinit();
@@ -606,11 +615,15 @@ loop:
break;
case ALOCALS:
+ if(skip)
+ goto casedef;
cursym->locals = p->to.offset;
pc++;
break;
case ATYPE:
+ if(skip)
+ goto casedef;
pc++;
goto loop;
diff --git a/src/cmd/6g/peep.c b/src/cmd/6g/peep.c
index 569655786..bb24d4144 100644
--- a/src/cmd/6g/peep.c
+++ b/src/cmd/6g/peep.c
@@ -472,6 +472,7 @@ elimshortmov(Reg *r)
{
Prog *p;
+ USED(r);
for(r=firstr; r!=R; r=r->link) {
p = r->prog;
if(regtyp(&p->to)) {
@@ -555,7 +556,7 @@ elimshortmov(Reg *r)
}
}
-int
+static int
regconsttyp(Adr *a)
{
if(regtyp(a))
@@ -758,7 +759,7 @@ subprop(Reg *r0)
}
}
if(debug['P'] && debug['v'])
- print("\tran off end; return 0\n", p);
+ print("\tran off end; return 0\n");
return 0;
gotit:
diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c
index c56d71678..7db6beab9 100644
--- a/src/cmd/6g/reg.c
+++ b/src/cmd/6g/reg.c
@@ -182,7 +182,6 @@ regopt(Prog *firstp)
return;
}
- r1 = R;
firstr = R;
lastr = R;
@@ -783,7 +782,7 @@ brk:
Var *v;
v = var + rgp->varno;
- print("registerize %N+%d (bit=%2d et=%2E) in %R\n",
+ print("registerize %N+%lld (bit=%2d et=%2E) in %R\n",
v->node, v->offset, rgp->varno, v->etype, rgp->regno);
}
paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
@@ -1061,7 +1060,7 @@ mkvar(Reg *r, Adr *a)
v->node = node;
if(debug['R'])
- print("bit=%2d et=%2E w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr);
+ print("bit=%2d et=%2E w=%d+%lld %#N %D flag=%d\n", i, et, o, w, node, a, v->addr);
ostats.nvar++;
diff --git a/src/cmd/6l/6.out.h b/src/cmd/6l/6.out.h
index 28de74944..237a802cd 100644
--- a/src/cmd/6l/6.out.h
+++ b/src/cmd/6l/6.out.h
@@ -866,6 +866,7 @@ enum
D_SIZE = D_INDIR + D_INDIR, /* 6l internal */
D_PCREL,
+ D_TLS,
T_TYPE = 1<<0,
T_INDEX = 1<<1,
diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c
index 07ba59ad9..8807a6ed5 100644
--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -310,6 +310,13 @@ elfreloc1(Reloc *r, vlong sectoff)
else
return -1;
break;
+
+ case D_TLS:
+ if(r->siz == 4)
+ VPUT(R_X86_64_TPOFF32 | (uint64)elfsym<<32);
+ else
+ return -1;
+ break;
}
VPUT(r->xadd);
diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h
index 1d6e55c97..4d481c69d 100644
--- a/src/cmd/6l/l.h
+++ b/src/cmd/6l/l.h
@@ -336,7 +336,6 @@ EXTERN Auto* curhist;
EXTERN Prog* curp;
EXTERN Sym* cursym;
EXTERN Sym* datap;
-EXTERN vlong elfdatsize;
EXTERN int debug[128];
EXTERN char literal[32];
EXTERN Sym* textp;
diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c
index 3775df9de..e98f91eeb 100644
--- a/src/cmd/6l/obj.c
+++ b/src/cmd/6l/obj.c
@@ -83,7 +83,7 @@ main(int argc, char *argv[])
INITRND = -1;
INITENTRY = 0;
LIBINITENTRY = 0;
- linkmode = LinkInternal; // TODO: LinkAuto once everything works.
+ linkmode = LinkAuto;
nuxiinit();
flagcount("1", "use alternate profiling code", &debug['1']);
@@ -107,6 +107,8 @@ main(int argc, char *argv[])
flagcount("a", "disassemble output", &debug['a']);
flagcount("c", "dump call graph", &debug['c']);
flagcount("d", "disable dynamic executable", &debug['d']);
+ flagstr("extld", "linker to run in external mode", &extld);
+ flagstr("extldflags", "flags for external linker", &extldflags);
flagcount("f", "ignore version mismatch", &debug['f']);
flagcount("g", "disable go package data checks", &debug['g']);
flagfn1("linkmode", "mode: set link mode (internal, external, auto)", setlinkmode);
@@ -133,11 +135,16 @@ main(int argc, char *argv[])
if(HEADTYPE == -1)
HEADTYPE = headtype(goos);
+ // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when
+ // Go was built; see ../../make.bash.
+ if(linkmode == LinkAuto && strcmp(getgoextlinkenabled(), "0") == 0)
+ linkmode = LinkInternal;
+
switch(HEADTYPE) {
default:
if(linkmode == LinkAuto)
linkmode = LinkInternal;
- if(linkmode == LinkExternal)
+ if(linkmode == LinkExternal && strcmp(getgoextlinkenabled(), "1") != 0)
sysfatal("cannot use -linkmode=external with -H %s", headstr(HEADTYPE));
break;
case Hdarwin:
@@ -597,11 +604,15 @@ loop:
goto loop;
case ALOCALS:
+ if(skip)
+ goto casdef;
cursym->locals = p->to.offset;
pc++;
goto loop;
case ATYPE:
+ if(skip)
+ goto casdef;
pc++;
goto loop;
diff --git a/src/cmd/6l/span.c b/src/cmd/6l/span.c
index fa94d8cf9..460a34f2f 100644
--- a/src/cmd/6l/span.c
+++ b/src/cmd/6l/span.c
@@ -32,6 +32,7 @@
#include "l.h"
#include "../ld/lib.h"
+#include "../ld/elf.h"
static int rexflag;
static int asmode;
@@ -880,7 +881,30 @@ putrelv:
r = addrel(cursym);
*r = rel;
r->off = curp->pc + andptr - and;
+ } else if(iself && linkmode == LinkExternal && a->type == D_INDIR+D_FS
+ && HEADTYPE != Hopenbsd) {
+ Reloc *r;
+ Sym *s;
+
+ r = addrel(cursym);
+ r->off = curp->pc + andptr - and;
+ r->add = 0;
+ r->xadd = 0;
+ r->siz = 4;
+ r->type = D_TLS;
+ if(a->offset == tlsoffset+0)
+ s = lookup("runtime.g", 0);
+ else
+ s = lookup("runtime.m", 0);
+ s->type = STLSBSS;
+ s->reachable = 1;
+ s->size = PtrSize;
+ s->hide = 1;
+ r->sym = s;
+ r->xsym = s;
+ v = 0;
}
+
put4(v);
return;
diff --git a/src/cmd/8a/a.y b/src/cmd/8a/a.y
index 516e5fbe7..246643427 100644
--- a/src/cmd/8a/a.y
+++ b/src/cmd/8a/a.y
@@ -507,6 +507,15 @@ omem:
$$.scale = $8;
checkscale($$.scale);
}
+| con '(' LLREG ')' '(' LSREG '*' con ')'
+ {
+ $$ = nullgen;
+ $$.type = D_INDIR+$3;
+ $$.offset = $1;
+ $$.index = $6;
+ $$.scale = $8;
+ checkscale($$.scale);
+ }
| '(' LLREG ')'
{
$$ = nullgen;
diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c
index b8fc2c03e..a7840f625 100644
--- a/src/cmd/8a/lex.c
+++ b/src/cmd/8a/lex.c
@@ -760,10 +760,12 @@ struct
"ORPS", LTYPE3, AORPS,
"PADDQ", LTYPE3, APADDQ,
"PAND", LTYPE3, APAND,
+ "PCMPEQB", LTYPE3, APCMPEQB,
"PMAXSW", LTYPE3, APMAXSW,
"PMAXUB", LTYPE3, APMAXUB,
"PMINSW", LTYPE3, APMINSW,
"PMINUB", LTYPE3, APMINUB,
+ "PMOVMSKB", LTYPE3, APMOVMSKB,
"PSADBW", LTYPE3, APSADBW,
"PSHUFB", LTYPE3, APSHUFB,
"PSUBB", LTYPE3, APSUBB,
diff --git a/src/cmd/8a/y.tab.c b/src/cmd/8a/y.tab.c
index 7eaff0bb0..6616c9139 100644
--- a/src/cmd/8a/y.tab.c
+++ b/src/cmd/8a/y.tab.c
@@ -1,21 +1,24 @@
-/* A Bison parser, made by GNU Bison 2.5. */
+/* A Bison parser, made by GNU Bison 2.3. */
-/* Bison implementation for Yacc-like parsers in C
-
- Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
-
- This program is free software: you can redistribute it and/or modify
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
@@ -26,7 +29,7 @@
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
-
+
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
@@ -44,7 +47,7 @@
#define YYBISON 1
/* Bison version. */
-#define YYBISON_VERSION "2.5"
+#define YYBISON_VERSION "2.3"
/* Skeleton name. */
#define YYSKELETON_NAME "yacc.c"
@@ -52,50 +55,11 @@
/* Pure parsers. */
#define YYPURE 0
-/* Push parsers. */
-#define YYPUSH 0
-
-/* Pull parsers. */
-#define YYPULL 1
-
/* Using locations. */
#define YYLSP_NEEDED 0
-/* Copy the first part of user declarations. */
-
-/* Line 268 of yacc.c */
-#line 31 "a.y"
-
-#include <u.h>
-#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */
-#include <libc.h>
-#include "a.h"
-
-
-/* Line 268 of yacc.c */
-#line 79 "y.tab.c"
-
-/* Enabling traces. */
-#ifndef YYDEBUG
-# define YYDEBUG 0
-#endif
-
-/* Enabling verbose error messages. */
-#ifdef YYERROR_VERBOSE
-# undef YYERROR_VERBOSE
-# define YYERROR_VERBOSE 1
-#else
-# define YYERROR_VERBOSE 0
-#endif
-
-/* Enabling the token table. */
-#ifndef YYTOKEN_TABLE
-# define YYTOKEN_TABLE 0
-#endif
-
-
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
@@ -171,13 +135,37 @@
+/* Copy the first part of user declarations. */
+#line 31 "a.y"
+
+#include <u.h>
+#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */
+#include <libc.h>
+#include "a.h"
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-{
-
-/* Line 293 of yacc.c */
#line 37 "a.y"
-
+{
Sym *sym;
int32 lval;
struct {
@@ -188,23 +176,22 @@ typedef union YYSTYPE
char sval[8];
Gen gen;
Gen2 gen2;
-
-
-
-/* Line 293 of yacc.c */
-#line 196 "y.tab.c"
-} YYSTYPE;
-# define YYSTYPE_IS_TRIVIAL 1
+}
+/* Line 193 of yacc.c. */
+#line 182 "y.tab.c"
+ YYSTYPE;
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
#endif
+
/* Copy the second part of user declarations. */
-/* Line 343 of yacc.c */
-#line 208 "y.tab.c"
+/* Line 216 of yacc.c. */
+#line 195 "y.tab.c"
#ifdef short
# undef short
@@ -279,14 +266,14 @@ typedef short int yytype_int16;
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static int
-YYID (int yyi)
+YYID (int i)
#else
static int
-YYID (yyi)
- int yyi;
+YYID (i)
+ int i;
#endif
{
- return yyi;
+ return i;
}
#endif
@@ -307,11 +294,11 @@ YYID (yyi)
# define alloca _alloca
# else
# define YYSTACK_ALLOC alloca
-# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef EXIT_SUCCESS
-# define EXIT_SUCCESS 0
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
# endif
# endif
# endif
@@ -334,24 +321,24 @@ YYID (yyi)
# ifndef YYSTACK_ALLOC_MAXIMUM
# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
# endif
-# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+# if (defined __cplusplus && ! defined _STDLIB_H \
&& ! ((defined YYMALLOC || defined malloc) \
&& (defined YYFREE || defined free)))
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef EXIT_SUCCESS
-# define EXIT_SUCCESS 0
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
# endif
# endif
# ifndef YYMALLOC
# define YYMALLOC malloc
-# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
# endif
# endif
# ifndef YYFREE
# define YYFREE free
-# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
void free (void *); /* INFRINGES ON USER NAME SPACE */
# endif
@@ -367,9 +354,9 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */
/* A type that is properly aligned for any stack member. */
union yyalloc
{
- yytype_int16 yyss_alloc;
- YYSTYPE yyvs_alloc;
-};
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ };
/* The size of the maximum gap between one aligned stack and the next. */
# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
@@ -380,27 +367,6 @@ union yyalloc
((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ YYSTACK_GAP_MAXIMUM)
-# define YYCOPY_NEEDED 1
-
-/* Relocate STACK from its old location to the new one. The
- local variables YYSIZE and YYSTACKSIZE give the old and new number of
- elements in the stack, and YYPTR gives the new location of the
- stack. Advance YYPTR to a properly aligned location for the next
- stack. */
-# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
- do \
- { \
- YYSIZE_T yynewbytes; \
- YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
- Stack = &yyptr->Stack_alloc; \
- yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
- yyptr += yynewbytes / sizeof (*yyptr); \
- } \
- while (YYID (0))
-
-#endif
-
-#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
/* Copy COUNT objects from FROM to TO. The source and destination do
not overlap. */
# ifndef YYCOPY
@@ -418,21 +384,38 @@ union yyalloc
while (YYID (0))
# endif
# endif
-#endif /* !YYCOPY_NEEDED */
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
/* YYFINAL -- State number of the termination state. */
#define YYFINAL 2
/* YYLAST -- Last index in YYTABLE. */
-#define YYLAST 553
+#define YYLAST 537
/* YYNTOKENS -- Number of terminals. */
#define YYNTOKENS 52
/* YYNNTS -- Number of nonterminals. */
#define YYNNTS 39
/* YYNRULES -- Number of rules. */
-#define YYNRULES 130
+#define YYNRULES 131
/* YYNRULES -- Number of states. */
-#define YYNSTATES 262
+#define YYNSTATES 266
/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
#define YYUNDEFTOK 2
@@ -490,10 +473,10 @@ static const yytype_uint16 yyprhs[] =
216, 218, 223, 226, 229, 231, 233, 235, 237, 239,
241, 244, 247, 250, 253, 258, 264, 268, 271, 273,
276, 280, 285, 287, 289, 291, 296, 301, 308, 318,
- 322, 326, 331, 337, 346, 348, 355, 361, 369, 370,
- 373, 376, 378, 380, 382, 384, 386, 389, 392, 395,
- 399, 401, 405, 409, 413, 417, 421, 426, 431, 435,
- 439
+ 328, 332, 336, 341, 347, 356, 358, 365, 371, 379,
+ 380, 383, 386, 388, 390, 392, 394, 396, 399, 402,
+ 405, 409, 411, 415, 419, 423, 427, 431, 436, 441,
+ 445, 449
};
/* YYRHS -- A `-1'-separated list of the rules' RHS. */
@@ -530,7 +513,8 @@ static const yytype_int8 yyrhs[] =
9, 29, 9, 29, -1, 84, -1, 85, -1, 89,
-1, 89, 48, 34, 49, -1, 89, 48, 40, 49,
-1, 89, 48, 34, 10, 89, 49, -1, 89, 48,
- 34, 49, 48, 34, 10, 89, 49, -1, 48, 34,
+ 34, 49, 48, 34, 10, 89, 49, -1, 89, 48,
+ 34, 49, 48, 35, 10, 89, 49, -1, 48, 34,
49, -1, 48, 40, 49, -1, 89, 48, 35, 49,
-1, 48, 34, 10, 89, 49, -1, 48, 34, 49,
48, 34, 10, 89, 49, -1, 86, -1, 86, 48,
@@ -559,10 +543,10 @@ static const yytype_uint16 yyrline[] =
331, 334, 340, 349, 358, 363, 368, 373, 378, 383,
390, 396, 407, 413, 419, 425, 431, 439, 448, 453,
458, 463, 470, 471, 474, 480, 486, 492, 501, 510,
- 515, 520, 526, 534, 544, 548, 557, 564, 573, 576,
- 580, 586, 587, 591, 594, 595, 599, 603, 607, 611,
- 617, 618, 622, 626, 630, 634, 638, 642, 646, 650,
- 654
+ 519, 524, 529, 535, 543, 553, 557, 566, 573, 582,
+ 585, 589, 595, 596, 600, 603, 604, 608, 612, 616,
+ 620, 626, 627, 631, 635, 639, 643, 647, 651, 655,
+ 659, 663
};
#endif
@@ -577,12 +561,11 @@ static const char *const yytname[] =
"LTYPEM", "LTYPEI", "LTYPEG", "LTYPEXC", "LTYPEX", "LCONST", "LFP",
"LPC", "LSB", "LBREG", "LLREG", "LSREG", "LFREG", "LXREG", "LFCONST",
"LSCONST", "LSP", "LNAME", "LLAB", "LVAR", "':'", "';'", "'='", "','",
- "'('", "')'", "'$'", "'~'", "$accept", "prog", "$@1", "line", "$@2",
- "$@3", "inst", "nonnon", "rimrem", "remrim", "rimnon", "nonrem",
- "nonrel", "spec1", "spec2", "spec3", "spec4", "spec5", "spec6", "spec7",
- "spec8", "spec9", "spec10", "rem", "rom", "rim", "rel", "reg", "imm",
- "imm2", "con2", "mem", "omem", "nmem", "nam", "offset", "pointer", "con",
- "expr", 0
+ "'('", "')'", "'$'", "'~'", "$accept", "prog", "@1", "line", "@2", "@3",
+ "inst", "nonnon", "rimrem", "remrim", "rimnon", "nonrem", "nonrel",
+ "spec1", "spec2", "spec3", "spec4", "spec5", "spec6", "spec7", "spec8",
+ "spec9", "spec10", "rem", "rom", "rim", "rel", "reg", "imm", "imm2",
+ "con2", "mem", "omem", "nmem", "nam", "offset", "pointer", "con", "expr", 0
};
#endif
@@ -613,10 +596,10 @@ static const yytype_uint8 yyr1[] =
77, 78, 78, 78, 79, 79, 79, 79, 79, 79,
80, 80, 80, 80, 80, 80, 80, 81, 82, 82,
82, 82, 83, 83, 84, 84, 84, 84, 84, 84,
- 84, 84, 84, 84, 85, 85, 86, 86, 87, 87,
- 87, 88, 88, 88, 89, 89, 89, 89, 89, 89,
- 90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
- 90
+ 84, 84, 84, 84, 84, 85, 85, 86, 86, 87,
+ 87, 87, 88, 88, 88, 89, 89, 89, 89, 89,
+ 89, 90, 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90
};
/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
@@ -631,45 +614,45 @@ static const yytype_uint8 yyr2[] =
1, 1, 1, 1, 2, 2, 1, 1, 1, 1,
1, 4, 2, 2, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 4, 5, 3, 2, 1, 2,
- 3, 4, 1, 1, 1, 4, 4, 6, 9, 3,
- 3, 4, 5, 8, 1, 6, 5, 7, 0, 2,
- 2, 1, 1, 1, 1, 1, 2, 2, 2, 3,
- 1, 3, 3, 3, 3, 3, 4, 4, 3, 3,
- 3
+ 3, 4, 1, 1, 1, 4, 4, 6, 9, 9,
+ 3, 3, 4, 5, 8, 1, 6, 5, 7, 0,
+ 2, 2, 1, 1, 1, 1, 1, 2, 2, 2,
+ 3, 1, 3, 3, 3, 3, 3, 4, 4, 3,
+ 3, 3
};
-/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
- Performed when YYTABLE doesn't specify something else to do. Zero
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
means the default is an error. */
static const yytype_uint8 yydefact[] =
{
2, 3, 1, 0, 0, 30, 0, 0, 0, 0,
0, 0, 30, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 9, 4, 0, 11, 31, 14,
- 0, 0, 114, 74, 76, 79, 75, 77, 78, 108,
- 115, 0, 0, 0, 15, 37, 60, 61, 92, 93,
- 104, 94, 0, 16, 69, 35, 70, 17, 0, 18,
- 0, 0, 108, 108, 0, 22, 45, 62, 66, 68,
- 67, 63, 94, 20, 0, 31, 47, 48, 23, 108,
+ 0, 0, 115, 74, 76, 79, 75, 77, 78, 109,
+ 116, 0, 0, 0, 15, 37, 60, 61, 92, 93,
+ 105, 94, 0, 16, 69, 35, 70, 17, 0, 18,
+ 0, 0, 109, 109, 0, 22, 45, 62, 66, 68,
+ 67, 63, 94, 20, 0, 31, 47, 48, 23, 109,
0, 0, 19, 39, 0, 0, 21, 0, 24, 0,
25, 0, 26, 54, 27, 0, 28, 0, 29, 0,
- 7, 0, 5, 0, 10, 117, 116, 0, 0, 0,
- 0, 36, 0, 0, 120, 0, 118, 0, 0, 0,
+ 7, 0, 5, 0, 10, 118, 117, 0, 0, 0,
+ 0, 36, 0, 0, 121, 0, 119, 0, 0, 0,
83, 82, 0, 81, 80, 34, 0, 0, 64, 65,
46, 72, 73, 0, 44, 0, 0, 72, 38, 0,
0, 0, 0, 0, 53, 0, 0, 0, 0, 12,
- 0, 13, 108, 109, 110, 0, 0, 99, 100, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 119,
+ 0, 13, 109, 110, 111, 0, 0, 100, 101, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 120,
0, 0, 0, 0, 86, 0, 0, 32, 33, 0,
0, 40, 0, 42, 0, 49, 51, 55, 56, 0,
- 0, 0, 8, 6, 0, 113, 111, 112, 0, 0,
- 0, 130, 129, 128, 0, 0, 121, 122, 123, 124,
- 125, 0, 0, 95, 101, 96, 0, 84, 71, 0,
+ 0, 0, 8, 6, 0, 114, 112, 113, 0, 0,
+ 0, 131, 130, 129, 0, 0, 122, 123, 124, 125,
+ 126, 0, 0, 95, 102, 96, 0, 84, 71, 0,
0, 88, 87, 0, 0, 0, 0, 0, 0, 0,
- 106, 102, 0, 126, 127, 0, 0, 0, 85, 41,
+ 107, 103, 0, 127, 128, 0, 0, 0, 85, 41,
89, 0, 43, 50, 52, 57, 58, 59, 0, 0,
- 105, 97, 0, 0, 90, 107, 0, 0, 91, 103,
- 0, 98
+ 106, 97, 0, 0, 0, 90, 108, 0, 0, 0,
+ 91, 104, 0, 0, 98, 99
};
/* YYDEFGOTO[NTERM-NUM]. */
@@ -686,47 +669,48 @@ static const yytype_int16 yydefgoto[] =
#define YYPACT_NINF -100
static const yytype_int16 yypact[] =
{
- -100, 22, -100, 165, -32, -22, 263, 286, 286, 332,
- 195, -1, 309, 212, 414, 286, 286, 286, 414, 81,
+ -100, 22, -100, 165, -32, -22, 265, 288, 288, 334,
+ 195, -1, 311, 212, 416, 288, 288, 288, 416, 81,
7, -16, 24, -12, -100, -100, -4, -100, -100, -100,
- 485, 485, -100, -100, -100, -100, -100, -100, -100, 39,
- -100, 332, 385, 485, -100, -100, -100, -100, -100, -100,
- 46, 65, 370, -100, -100, 72, -100, -100, 75, -100,
- 76, 332, 39, 102, 240, -100, -100, -100, -100, -100,
- -100, -100, 77, -100, 117, 332, -100, -100, -100, 102,
- 408, 485, -100, -100, 83, 85, -100, 89, -100, 91,
- -100, 92, -100, 97, -100, 98, -100, 100, -100, 101,
- -100, 485, -100, 485, -100, -100, -100, 135, 485, 485,
- 104, -100, -6, 113, -100, 71, -100, 119, 32, 423,
- -100, -100, 438, -100, -100, -100, 332, 286, -100, -100,
- -100, 104, -100, 355, -100, 29, 485, -100, -100, 408,
- 146, 455, 332, 332, 332, 461, 332, 332, 165, 164,
- 165, 164, 102, -100, -100, 6, 485, 161, -100, 485,
- 485, 485, 206, 207, 485, 485, 485, 485, 485, -100,
- 203, 4, 166, 167, -100, 470, 173, -100, -100, 174,
- 178, -100, 15, -100, 179, 183, 189, -100, -100, 187,
- 193, 197, -100, -100, 204, -100, -100, -100, 202, 208,
- 222, 533, 541, 78, 485, 485, 95, 95, -100, -100,
- -100, 485, 485, 210, -100, -100, 215, -100, -100, 7,
- 232, 256, -100, 216, 233, 235, 7, 485, 81, 236,
- -100, -100, 268, 188, 188, 230, 237, 250, -100, -100,
- 276, 258, -100, -100, -100, -100, -100, -100, 244, 485,
- -100, -100, 279, 272, -100, -100, 253, 485, -100, -100,
- 259, -100
+ 469, 469, -100, -100, -100, -100, -100, -100, -100, 39,
+ -100, 334, 387, 469, -100, -100, -100, -100, -100, -100,
+ 46, 65, 372, -100, -100, 72, -100, -100, 83, -100,
+ 86, 334, 39, 102, 242, -100, -100, -100, -100, -100,
+ -100, -100, 77, -100, 117, 334, -100, -100, -100, 102,
+ 410, 469, -100, -100, 89, 90, -100, 92, -100, 97,
+ -100, 98, -100, 100, -100, 101, -100, 105, -100, 106,
+ -100, 469, -100, 469, -100, -100, -100, 135, 469, 469,
+ 114, -100, -6, 128, -100, 71, -100, 175, 32, 218,
+ -100, -100, 425, -100, -100, -100, 334, 288, -100, -100,
+ -100, 114, -100, 357, -100, 29, 469, -100, -100, 410,
+ 181, 440, 334, 334, 334, 457, 334, 334, 165, 164,
+ 165, 164, 102, -100, -100, 6, 469, 166, -100, 469,
+ 469, 469, 207, 208, 469, 469, 469, 469, 469, -100,
+ 206, 4, 173, 174, -100, 463, 176, -100, -100, 184,
+ 187, -100, 15, -100, 193, 200, 213, -100, -100, 211,
+ 217, 220, -100, -100, 222, -100, -100, -100, 216, 219,
+ 238, 517, 525, 78, 469, 469, 95, 95, -100, -100,
+ -100, 469, 469, 232, -100, -100, 237, -100, -100, 7,
+ 252, 278, -100, 239, 254, 256, 7, 469, 81, 263,
+ -100, -100, 293, 188, 188, 255, 258, 88, -100, -100,
+ 300, 281, -100, -100, -100, -100, -100, -100, 262, 469,
+ -100, -100, 304, 305, 289, -100, -100, 277, 469, 469,
+ -100, -100, 283, 284, -100, -100
};
/* YYPGOTO[NTERM-NUM]. */
static const yytype_int16 yypgoto[] =
{
- -100, -100, -100, -99, -100, -100, -100, 293, -100, -100,
- -100, 295, -100, -100, -100, -100, -100, -100, -100, -100,
- -100, -100, -100, 17, 245, 0, -7, -9, -8, 90,
+ -100, -100, -100, -99, -100, -100, -100, 315, -100, -100,
+ -100, 318, -100, -100, -100, -100, -100, -100, -100, -100,
+ -100, -100, -100, 17, 270, 0, -7, -9, -8, 112,
-100, 13, 1, -3, -2, -44, -100, -10, -64
};
/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
positive, shift that token. If negative, reduce the rule which
- number is the opposite. If YYTABLE_NINF, syntax error. */
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
#define YYTABLE_NINF -1
static const yytype_uint16 yytable[] =
{
@@ -742,58 +726,50 @@ static const yytype_uint16 yytable[] =
168, 114, 111, 114, 117, 201, 202, 203, 153, 154,
206, 207, 208, 209, 210, 166, 167, 168, 194, 106,
108, 109, 114, 118, 33, 34, 35, 36, 37, 125,
- 169, 38, 126, 127, 128, 135, 180, 178, 136, 85,
- 139, 184, 181, 140, 129, 189, 141, 188, 142, 143,
- 233, 234, 152, 177, 144, 145, 199, 146, 147, 114,
- 114, 114, 155, 170, 114, 114, 114, 114, 114, 185,
- 186, 187, 158, 190, 191, 106, 4, 159, 160, 161,
- 162, 163, 164, 165, 166, 167, 168, 179, 5, 6,
+ 169, 38, 252, 253, 128, 135, 180, 178, 136, 85,
+ 126, 184, 181, 127, 129, 189, 139, 188, 140, 141,
+ 233, 234, 152, 177, 142, 143, 199, 144, 145, 114,
+ 114, 114, 146, 147, 114, 114, 114, 114, 114, 185,
+ 186, 187, 155, 190, 191, 106, 4, 159, 160, 161,
+ 162, 163, 164, 165, 166, 167, 168, 158, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 114, 114, 164, 165, 166, 167,
- 168, 235, 236, 30, 31, 61, 21, 22, 23, 200,
- 24, 239, 204, 211, 205, 214, 215, 246, 245, 247,
- 30, 31, 217, 218, 32, 219, 223, 224, 33, 34,
- 35, 36, 37, 225, 226, 38, 62, 63, 40, 256,
- 227, 32, 64, 42, 228, 52, 43, 260, 30, 31,
- 133, 230, 229, 79, 63, 40, 232, 231, 237, 80,
- 81, 240, 52, 43, 238, 241, 182, 243, 248, 32,
- 244, 30, 31, 33, 34, 35, 36, 37, 249, 250,
- 38, 62, 63, 40, 252, 253, 251, 254, 42, 257,
- 52, 43, 32, 255, 30, 31, 33, 34, 35, 36,
- 37, 258, 259, 38, 39, 76, 40, 77, 261, 134,
- 41, 42, 0, 242, 43, 32, 0, 30, 31, 33,
- 34, 35, 36, 37, 0, 0, 38, 39, 0, 40,
- 0, 0, 0, 0, 42, 0, 52, 43, 32, 0,
- 30, 31, 33, 34, 35, 36, 37, 0, 0, 38,
- 39, 0, 40, 0, 0, 0, 75, 42, 0, 0,
- 43, 32, 0, 30, 31, 33, 34, 35, 36, 37,
- 0, 0, 38, 39, 0, 40, 0, 0, 30, 119,
- 42, 0, 0, 43, 32, 0, 0, 0, 33, 34,
- 35, 36, 37, 30, 31, 38, 0, 0, 40, 32,
- 0, 0, 0, 42, 0, 0, 43, 0, 120, 121,
- 0, 39, 0, 40, 32, 0, 30, 31, 122, 112,
- 0, 43, 30, 31, 0, 113, 0, 0, 40, 0,
- 0, 30, 31, 81, 0, 0, 43, 32, 0, 0,
- 0, 0, 0, 32, 0, 0, 30, 175, 0, 79,
- 63, 40, 32, 0, 0, 39, 81, 40, 0, 43,
- 0, 174, 42, 30, 31, 43, 40, 32, 0, 30,
- 31, 81, 0, 0, 43, 0, 176, 0, 30, 31,
- 0, 40, 0, 0, 32, 0, 81, 0, 0, 43,
- 32, 0, 0, 30, 31, 0, 0, 0, 40, 32,
- 0, 0, 0, 81, 40, 182, 43, 0, 216, 81,
- 0, 52, 43, 40, 32, 0, 0, 0, 81, 0,
- 0, 43, 0, 0, 0, 0, 0, 0, 40, 0,
- 0, 0, 0, 81, 0, 0, 43, 160, 161, 162,
- 163, 164, 165, 166, 167, 168, 161, 162, 163, 164,
- 165, 166, 167, 168
+ 168, 235, 236, 30, 31, 61, 21, 22, 23, 170,
+ 24, 239, 179, 204, 200, 205, 211, 246, 245, 247,
+ 30, 31, 214, 215, 32, 217, 30, 31, 33, 34,
+ 35, 36, 37, 218, 219, 38, 62, 63, 40, 257,
+ 223, 32, 64, 42, 224, 52, 43, 32, 262, 263,
+ 30, 31, 133, 79, 63, 40, 174, 225, 226, 80,
+ 81, 40, 52, 43, 227, 230, 81, 228, 231, 43,
+ 229, 32, 232, 30, 31, 33, 34, 35, 36, 37,
+ 237, 240, 38, 62, 63, 40, 238, 241, 243, 182,
+ 42, 244, 52, 43, 32, 248, 30, 31, 33, 34,
+ 35, 36, 37, 249, 250, 38, 39, 251, 40, 254,
+ 255, 256, 41, 42, 258, 259, 43, 32, 260, 30,
+ 31, 33, 34, 35, 36, 37, 261, 76, 38, 39,
+ 77, 40, 264, 265, 134, 242, 42, 0, 52, 43,
+ 32, 0, 30, 31, 33, 34, 35, 36, 37, 0,
+ 0, 38, 39, 0, 40, 0, 0, 0, 75, 42,
+ 0, 0, 43, 32, 0, 30, 31, 33, 34, 35,
+ 36, 37, 0, 0, 38, 39, 0, 40, 0, 0,
+ 30, 119, 42, 0, 0, 43, 32, 0, 0, 0,
+ 33, 34, 35, 36, 37, 30, 31, 38, 0, 0,
+ 40, 32, 0, 0, 0, 42, 0, 0, 43, 0,
+ 120, 121, 0, 39, 0, 40, 32, 0, 30, 31,
+ 122, 112, 0, 43, 30, 31, 0, 113, 0, 0,
+ 40, 0, 0, 30, 175, 81, 0, 0, 43, 32,
+ 0, 0, 0, 0, 0, 32, 0, 0, 30, 31,
+ 0, 79, 63, 40, 32, 0, 0, 39, 81, 40,
+ 0, 43, 0, 176, 42, 30, 31, 43, 40, 32,
+ 0, 30, 31, 81, 0, 0, 43, 30, 31, 0,
+ 0, 0, 0, 40, 0, 0, 32, 0, 81, 0,
+ 182, 43, 32, 0, 0, 0, 0, 0, 32, 0,
+ 40, 216, 0, 0, 0, 81, 40, 52, 43, 0,
+ 0, 81, 40, 0, 43, 0, 0, 81, 0, 0,
+ 43, 160, 161, 162, 163, 164, 165, 166, 167, 168,
+ 161, 162, 163, 164, 165, 166, 167, 168
};
-#define yypact_value_is_default(yystate) \
- ((yystate) == (-100))
-
-#define yytable_value_is_error(yytable_value) \
- YYID (0)
-
static const yytype_int16 yycheck[] =
{
10, 10, 10, 13, 10, 13, 13, 10, 8, 11,
@@ -808,50 +784,48 @@ static const yytype_int16 yycheck[] =
12, 101, 75, 103, 48, 159, 160, 161, 108, 109,
164, 165, 166, 167, 168, 10, 11, 12, 152, 119,
8, 9, 122, 48, 33, 34, 35, 36, 37, 47,
- 49, 40, 47, 47, 133, 48, 136, 127, 11, 139,
- 47, 141, 139, 48, 133, 145, 47, 145, 47, 47,
+ 49, 40, 34, 35, 133, 48, 136, 127, 11, 139,
+ 47, 141, 139, 47, 133, 145, 47, 145, 48, 47,
204, 205, 7, 126, 47, 47, 156, 47, 47, 159,
- 160, 161, 48, 34, 164, 165, 166, 167, 168, 142,
- 143, 144, 49, 146, 147, 175, 1, 3, 4, 5,
- 6, 7, 8, 9, 10, 11, 12, 31, 13, 14,
+ 160, 161, 47, 47, 164, 165, 166, 167, 168, 142,
+ 143, 144, 48, 146, 147, 175, 1, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 49, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 204, 205, 8, 9, 10, 11,
- 12, 211, 212, 8, 9, 10, 41, 42, 43, 48,
- 45, 219, 6, 10, 7, 49, 49, 227, 226, 228,
- 8, 9, 49, 49, 29, 47, 47, 44, 33, 34,
- 35, 36, 37, 44, 47, 40, 41, 42, 43, 249,
- 47, 29, 47, 48, 47, 50, 51, 257, 8, 9,
- 10, 49, 48, 41, 42, 43, 34, 49, 48, 47,
- 48, 29, 50, 51, 49, 9, 50, 34, 32, 29,
- 35, 8, 9, 33, 34, 35, 36, 37, 10, 49,
- 40, 41, 42, 43, 34, 9, 49, 29, 48, 10,
- 50, 51, 29, 49, 8, 9, 33, 34, 35, 36,
- 37, 29, 49, 40, 41, 12, 43, 12, 49, 64,
- 47, 48, -1, 223, 51, 29, -1, 8, 9, 33,
- 34, 35, 36, 37, -1, -1, 40, 41, -1, 43,
- -1, -1, -1, -1, 48, -1, 50, 51, 29, -1,
- 8, 9, 33, 34, 35, 36, 37, -1, -1, 40,
- 41, -1, 43, -1, -1, -1, 47, 48, -1, -1,
- 51, 29, -1, 8, 9, 33, 34, 35, 36, 37,
- -1, -1, 40, 41, -1, 43, -1, -1, 8, 9,
- 48, -1, -1, 51, 29, -1, -1, -1, 33, 34,
- 35, 36, 37, 8, 9, 40, -1, -1, 43, 29,
- -1, -1, -1, 48, -1, -1, 51, -1, 38, 39,
- -1, 41, -1, 43, 29, -1, 8, 9, 48, 34,
- -1, 51, 8, 9, -1, 40, -1, -1, 43, -1,
- -1, 8, 9, 48, -1, -1, 51, 29, -1, -1,
- -1, -1, -1, 29, -1, -1, 8, 9, -1, 41,
- 42, 43, 29, -1, -1, 41, 48, 43, -1, 51,
- -1, 38, 48, 8, 9, 51, 43, 29, -1, 8,
- 9, 48, -1, -1, 51, -1, 38, -1, 8, 9,
- -1, 43, -1, -1, 29, -1, 48, -1, -1, 51,
- 29, -1, -1, 8, 9, -1, -1, -1, 43, 29,
- -1, -1, -1, 48, 43, 50, 51, -1, 38, 48,
- -1, 50, 51, 43, 29, -1, -1, -1, 48, -1,
- -1, 51, -1, -1, -1, -1, -1, -1, 43, -1,
- -1, -1, -1, 48, -1, -1, 51, 4, 5, 6,
- 7, 8, 9, 10, 11, 12, 5, 6, 7, 8,
- 9, 10, 11, 12
+ 12, 211, 212, 8, 9, 10, 41, 42, 43, 34,
+ 45, 219, 31, 6, 48, 7, 10, 227, 226, 228,
+ 8, 9, 49, 49, 29, 49, 8, 9, 33, 34,
+ 35, 36, 37, 49, 47, 40, 41, 42, 43, 249,
+ 47, 29, 47, 48, 44, 50, 51, 29, 258, 259,
+ 8, 9, 10, 41, 42, 43, 38, 44, 47, 47,
+ 48, 43, 50, 51, 47, 49, 48, 47, 49, 51,
+ 48, 29, 34, 8, 9, 33, 34, 35, 36, 37,
+ 48, 29, 40, 41, 42, 43, 49, 9, 34, 50,
+ 48, 35, 50, 51, 29, 32, 8, 9, 33, 34,
+ 35, 36, 37, 10, 49, 40, 41, 49, 43, 9,
+ 29, 49, 47, 48, 10, 10, 51, 29, 29, 8,
+ 9, 33, 34, 35, 36, 37, 49, 12, 40, 41,
+ 12, 43, 49, 49, 64, 223, 48, -1, 50, 51,
+ 29, -1, 8, 9, 33, 34, 35, 36, 37, -1,
+ -1, 40, 41, -1, 43, -1, -1, -1, 47, 48,
+ -1, -1, 51, 29, -1, 8, 9, 33, 34, 35,
+ 36, 37, -1, -1, 40, 41, -1, 43, -1, -1,
+ 8, 9, 48, -1, -1, 51, 29, -1, -1, -1,
+ 33, 34, 35, 36, 37, 8, 9, 40, -1, -1,
+ 43, 29, -1, -1, -1, 48, -1, -1, 51, -1,
+ 38, 39, -1, 41, -1, 43, 29, -1, 8, 9,
+ 48, 34, -1, 51, 8, 9, -1, 40, -1, -1,
+ 43, -1, -1, 8, 9, 48, -1, -1, 51, 29,
+ -1, -1, -1, -1, -1, 29, -1, -1, 8, 9,
+ -1, 41, 42, 43, 29, -1, -1, 41, 48, 43,
+ -1, 51, -1, 38, 48, 8, 9, 51, 43, 29,
+ -1, 8, 9, 48, -1, -1, 51, 8, 9, -1,
+ -1, -1, -1, 43, -1, -1, 29, -1, 48, -1,
+ 50, 51, 29, -1, -1, -1, -1, -1, 29, -1,
+ 43, 38, -1, -1, -1, 48, 43, 50, 51, -1,
+ -1, 48, 43, -1, 51, -1, -1, 48, -1, -1,
+ 51, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 5, 6, 7, 8, 9, 10, 11, 12
};
/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
@@ -883,8 +857,8 @@ static const yytype_uint8 yystos[] =
9, 29, 82, 47, 44, 44, 47, 47, 47, 48,
49, 49, 34, 90, 90, 89, 89, 48, 49, 80,
29, 9, 81, 34, 35, 80, 89, 79, 32, 10,
- 49, 49, 34, 9, 29, 49, 89, 10, 29, 49,
- 89, 49
+ 49, 49, 34, 35, 9, 29, 49, 89, 10, 10,
+ 29, 49, 89, 89, 49, 49
};
#define yyerrok (yyerrstatus = 0)
@@ -899,18 +873,9 @@ static const yytype_uint8 yystos[] =
/* Like YYERROR except do call yyerror. This remains here temporarily
to ease the transition to the new meaning of YYERROR, for GCC.
- Once GCC version 2 has supplanted version 1, this can go. However,
- YYFAIL appears to be in use. Nevertheless, it is formally deprecated
- in Bison 2.4.2's NEWS entry, where a plan to phase it out is
- discussed. */
+ Once GCC version 2 has supplanted version 1, this can go. */
#define YYFAIL goto yyerrlab
-#if defined YYFAIL
- /* This is here to suppress warnings from the GCC cpp's
- -Wunused-macros. Normally we don't worry about that warning, but
- some users do, and we want to make it easy for users to remove
- YYFAIL uses, which will produce warnings from Bison 2.5. */
-#endif
#define YYRECOVERING() (!!yyerrstatus)
@@ -920,6 +885,7 @@ do \
{ \
yychar = (Token); \
yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
YYPOPSTACK (1); \
goto yybackup; \
} \
@@ -961,10 +927,19 @@ while (YYID (0))
#endif
-/* This macro is provided for backward compatibility. */
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
#ifndef YY_LOCATION_PRINT
-# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
#endif
@@ -1068,20 +1043,17 @@ yy_symbol_print (yyoutput, yytype, yyvaluep)
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static void
-yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
#else
static void
-yy_stack_print (yybottom, yytop)
- yytype_int16 *yybottom;
- yytype_int16 *yytop;
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
#endif
{
YYFPRINTF (stderr, "Stack now");
- for (; yybottom <= yytop; yybottom++)
- {
- int yybot = *yybottom;
- YYFPRINTF (stderr, " %d", yybot);
- }
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
YYFPRINTF (stderr, "\n");
}
@@ -1115,11 +1087,11 @@ yy_reduce_print (yyvsp, yyrule)
/* The symbols being reduced. */
for (yyi = 0; yyi < yynrhs; yyi++)
{
- YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ fprintf (stderr, " $%d = ", yyi + 1);
yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
&(yyvsp[(yyi + 1) - (yynrhs)])
);
- YYFPRINTF (stderr, "\n");
+ fprintf (stderr, "\n");
}
}
@@ -1156,6 +1128,7 @@ int yydebug;
# define YYMAXDEPTH 10000
#endif
+
#if YYERROR_VERBOSE
@@ -1258,142 +1231,115 @@ yytnamerr (char *yyres, const char *yystr)
}
# endif
-/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
- about the unexpected token YYTOKEN for the state stack whose top is
- YYSSP.
-
- Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
- not large enough to hold the message. In that case, also set
- *YYMSG_ALLOC to the required number of bytes. Return 2 if the
- required number of bytes is too large to store. */
-static int
-yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
- yytype_int16 *yyssp, int yytoken)
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
{
- YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]);
- YYSIZE_T yysize = yysize0;
- YYSIZE_T yysize1;
- enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
- /* Internationalized format string. */
- const char *yyformat = 0;
- /* Arguments of yyformat. */
- char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
- /* Number of reported tokens (one for the "unexpected", one per
- "expected"). */
- int yycount = 0;
-
- /* There are many possibilities here to consider:
- - Assume YYFAIL is not used. It's too flawed to consider. See
- <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
- for details. YYERROR is fine as it does not invoke this
- function.
- - If this state is a consistent state with a default action, then
- the only way this function was invoked is if the default action
- is an error action. In that case, don't check for expected
- tokens because there are none.
- - The only way there can be no lookahead present (in yychar) is if
- this state is a consistent state with a default action. Thus,
- detecting the absence of a lookahead is sufficient to determine
- that there is no unexpected or expected token to report. In that
- case, just report a simple "syntax error".
- - Don't assume there isn't a lookahead just because this state is a
- consistent state with a default action. There might have been a
- previous inconsistent state, consistent state with a non-default
- action, or user semantic action that manipulated yychar.
- - Of course, the expected token list depends on states to have
- correct lookahead information, and it depends on the parser not
- to perform extra reductions after fetching a lookahead from the
- scanner and before detecting a syntax error. Thus, state merging
- (from LALR or IELR) and default reductions corrupt the expected
- token list. However, the list is correct for canonical LR with
- one exception: it will still contain any token that will not be
- accepted due to an error action in a later state.
- */
- if (yytoken != YYEMPTY)
- {
- int yyn = yypact[*yyssp];
- yyarg[yycount++] = yytname[yytoken];
- if (!yypact_value_is_default (yyn))
- {
- /* Start YYX at -YYN if negative to avoid negative indexes in
- YYCHECK. In other words, skip the first -YYN actions for
- this state because they are default actions. */
- int yyxbegin = yyn < 0 ? -yyn : 0;
- /* Stay within bounds of both yycheck and yytname. */
- int yychecklim = YYLAST - yyn + 1;
- int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
- int yyx;
-
- for (yyx = yyxbegin; yyx < yyxend; ++yyx)
- if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
- && !yytable_value_is_error (yytable[yyx + yyn]))
- {
- if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
- {
- yycount = 1;
- yysize = yysize0;
- break;
- }
- yyarg[yycount++] = yytname[yyx];
- yysize1 = yysize + yytnamerr (0, yytname[yyx]);
- if (! (yysize <= yysize1
- && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
- return 2;
- yysize = yysize1;
- }
- }
- }
+ int yyn = yypact[yystate];
- switch (yycount)
- {
-# define YYCASE_(N, S) \
- case N: \
- yyformat = S; \
- break
- YYCASE_(0, YY_("syntax error"));
- YYCASE_(1, YY_("syntax error, unexpected %s"));
- YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
- YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
- YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
- YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
-# undef YYCASE_
- }
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
- yysize1 = yysize + yystrlen (yyformat);
- if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
- return 2;
- yysize = yysize1;
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
- if (*yymsg_alloc < yysize)
- {
- *yymsg_alloc = 2 * yysize;
- if (! (yysize <= *yymsg_alloc
- && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
- *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
- return 1;
- }
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
- /* Avoid sprintf, as that infringes on the user's name space.
- Don't have undefined behavior even if the translation
- produced a string with the wrong number of "%s"s. */
- {
- char *yyp = *yymsg;
- int yyi = 0;
- while ((*yyp = *yyformat) != '\0')
- if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
- {
- yyp += yytnamerr (yyp, yyarg[yyi++]);
- yyformat += 2;
- }
- else
- {
- yyp++;
- yyformat++;
- }
- }
- return 0;
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
}
#endif /* YYERROR_VERBOSE */
+
/*-----------------------------------------------.
| Release the memory associated to this symbol. |
@@ -1425,9 +1371,10 @@ yydestruct (yymsg, yytype, yyvaluep)
break;
}
}
-
+
/* Prevent warnings from -Wmissing-prototypes. */
+
#ifdef YYPARSE_PARAM
#if defined __STDC__ || defined __cplusplus
int yyparse (void *YYPARSE_PARAM);
@@ -1443,16 +1390,18 @@ int yyparse ();
#endif /* ! YYPARSE_PARAM */
-/* The lookahead symbol. */
+
+/* The look-ahead symbol. */
int yychar;
-/* The semantic value of the lookahead symbol. */
+/* The semantic value of the look-ahead symbol. */
YYSTYPE yylval;
/* Number of syntax errors so far. */
int yynerrs;
+
/*----------.
| yyparse. |
`----------*/
@@ -1479,37 +1428,14 @@ yyparse ()
#endif
#endif
{
- int yystate;
- /* Number of tokens to shift before error messages enabled. */
- int yyerrstatus;
-
- /* The stacks and their tools:
- `yyss': related to states.
- `yyvs': related to semantic values.
-
- Refer to the stacks thru separate pointers, to allow yyoverflow
- to reallocate them elsewhere. */
-
- /* The state stack. */
- yytype_int16 yyssa[YYINITDEPTH];
- yytype_int16 *yyss;
- yytype_int16 *yyssp;
-
- /* The semantic value stack. */
- YYSTYPE yyvsa[YYINITDEPTH];
- YYSTYPE *yyvs;
- YYSTYPE *yyvsp;
-
- YYSIZE_T yystacksize;
-
+
+ int yystate;
int yyn;
int yyresult;
- /* Lookahead token as an internal (translated) token number. */
- int yytoken;
- /* The variables used to return semantic value and location from the
- action routines. */
- YYSTYPE yyval;
-
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
#if YYERROR_VERBOSE
/* Buffer for error messages, and its allocated size. */
char yymsgbuf[128];
@@ -1517,28 +1443,51 @@ yyparse ()
YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
#endif
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+
+
#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
/* The number of symbols on the RHS of the reduced rule.
Keep to zero when no symbol should be popped. */
int yylen = 0;
- yytoken = 0;
- yyss = yyssa;
- yyvs = yyvsa;
- yystacksize = YYINITDEPTH;
-
YYDPRINTF ((stderr, "Starting parse\n"));
yystate = 0;
yyerrstatus = 0;
yynerrs = 0;
- yychar = YYEMPTY; /* Cause a token to be read. */
+ yychar = YYEMPTY; /* Cause a token to be read. */
/* Initialize stack pointers.
Waste one element of value and location stack
so that they stay on the same level as the state stack.
The wasted elements are never initialized. */
+
yyssp = yyss;
yyvsp = yyvs;
@@ -1568,6 +1517,7 @@ yyparse ()
YYSTYPE *yyvs1 = yyvs;
yytype_int16 *yyss1 = yyss;
+
/* Each stack pointer address is followed by the size of the
data in use in that stack, in bytes. This used to be a
conditional around just the two extra args, but that might
@@ -1575,6 +1525,7 @@ yyparse ()
yyoverflow (YY_("memory exhausted"),
&yyss1, yysize * sizeof (*yyssp),
&yyvs1, yysize * sizeof (*yyvsp),
+
&yystacksize);
yyss = yyss1;
@@ -1597,8 +1548,9 @@ yyparse ()
(union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
if (! yyptr)
goto yyexhaustedlab;
- YYSTACK_RELOCATE (yyss_alloc, yyss);
- YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
# undef YYSTACK_RELOCATE
if (yyss1 != yyssa)
YYSTACK_FREE (yyss1);
@@ -1609,6 +1561,7 @@ yyparse ()
yyssp = yyss + yysize - 1;
yyvsp = yyvs + yysize - 1;
+
YYDPRINTF ((stderr, "Stack size increased to %lu\n",
(unsigned long int) yystacksize));
@@ -1618,9 +1571,6 @@ yyparse ()
YYDPRINTF ((stderr, "Entering state %d\n", yystate));
- if (yystate == YYFINAL)
- YYACCEPT;
-
goto yybackup;
/*-----------.
@@ -1629,16 +1579,16 @@ yyparse ()
yybackup:
/* Do appropriate processing given the current state. Read a
- lookahead token if we need one and don't already have one. */
+ look-ahead token if we need one and don't already have one. */
- /* First try to decide what to do without reference to lookahead token. */
+ /* First try to decide what to do without reference to look-ahead token. */
yyn = yypact[yystate];
- if (yypact_value_is_default (yyn))
+ if (yyn == YYPACT_NINF)
goto yydefault;
- /* Not known => get a lookahead token if don't already have one. */
+ /* Not known => get a look-ahead token if don't already have one. */
- /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
if (yychar == YYEMPTY)
{
YYDPRINTF ((stderr, "Reading a token: "));
@@ -1664,22 +1614,26 @@ yybackup:
yyn = yytable[yyn];
if (yyn <= 0)
{
- if (yytable_value_is_error (yyn))
- goto yyerrlab;
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
yyn = -yyn;
goto yyreduce;
}
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
/* Count tokens shifted since error; after three, turn off error
status. */
if (yyerrstatus)
yyerrstatus--;
- /* Shift the lookahead token. */
+ /* Shift the look-ahead token. */
YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
- /* Discard the shifted token. */
- yychar = YYEMPTY;
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
yystate = yyn;
*++yyvsp = yylval;
@@ -1719,8 +1673,6 @@ yyreduce:
switch (yyn)
{
case 3:
-
-/* Line 1806 of yacc.c */
#line 70 "a.y"
{
stmtline = lineno;
@@ -1728,8 +1680,6 @@ yyreduce:
break;
case 5:
-
-/* Line 1806 of yacc.c */
#line 77 "a.y"
{
if((yyvsp[(1) - (2)].sym)->value != pc)
@@ -1739,8 +1689,6 @@ yyreduce:
break;
case 7:
-
-/* Line 1806 of yacc.c */
#line 84 "a.y"
{
(yyvsp[(1) - (2)].sym)->type = LLAB;
@@ -1749,8 +1697,6 @@ yyreduce:
break;
case 12:
-
-/* Line 1806 of yacc.c */
#line 95 "a.y"
{
(yyvsp[(1) - (3)].sym)->type = LVAR;
@@ -1759,8 +1705,6 @@ yyreduce:
break;
case 13:
-
-/* Line 1806 of yacc.c */
#line 100 "a.y"
{
if((yyvsp[(1) - (3)].sym)->value != (yyvsp[(3) - (3)].lval))
@@ -1770,120 +1714,86 @@ yyreduce:
break;
case 14:
-
-/* Line 1806 of yacc.c */
#line 105 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 15:
-
-/* Line 1806 of yacc.c */
#line 106 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 16:
-
-/* Line 1806 of yacc.c */
#line 107 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 17:
-
-/* Line 1806 of yacc.c */
#line 108 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 18:
-
-/* Line 1806 of yacc.c */
#line 109 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 19:
-
-/* Line 1806 of yacc.c */
#line 110 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 20:
-
-/* Line 1806 of yacc.c */
#line 111 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 21:
-
-/* Line 1806 of yacc.c */
#line 112 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 22:
-
-/* Line 1806 of yacc.c */
#line 113 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 23:
-
-/* Line 1806 of yacc.c */
#line 114 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 24:
-
-/* Line 1806 of yacc.c */
#line 115 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 25:
-
-/* Line 1806 of yacc.c */
#line 116 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 26:
-
-/* Line 1806 of yacc.c */
#line 117 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 27:
-
-/* Line 1806 of yacc.c */
#line 118 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 28:
-
-/* Line 1806 of yacc.c */
#line 119 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 29:
-
-/* Line 1806 of yacc.c */
#line 120 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 30:
-
-/* Line 1806 of yacc.c */
#line 123 "a.y"
{
(yyval.gen2).from = nullgen;
@@ -1892,8 +1802,6 @@ yyreduce:
break;
case 31:
-
-/* Line 1806 of yacc.c */
#line 128 "a.y"
{
(yyval.gen2).from = nullgen;
@@ -1902,8 +1810,6 @@ yyreduce:
break;
case 32:
-
-/* Line 1806 of yacc.c */
#line 135 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
@@ -1912,8 +1818,6 @@ yyreduce:
break;
case 33:
-
-/* Line 1806 of yacc.c */
#line 142 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
@@ -1922,8 +1826,6 @@ yyreduce:
break;
case 34:
-
-/* Line 1806 of yacc.c */
#line 149 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (2)].gen);
@@ -1932,8 +1834,6 @@ yyreduce:
break;
case 35:
-
-/* Line 1806 of yacc.c */
#line 154 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (1)].gen);
@@ -1942,8 +1842,6 @@ yyreduce:
break;
case 36:
-
-/* Line 1806 of yacc.c */
#line 161 "a.y"
{
(yyval.gen2).from = nullgen;
@@ -1952,8 +1850,6 @@ yyreduce:
break;
case 37:
-
-/* Line 1806 of yacc.c */
#line 166 "a.y"
{
(yyval.gen2).from = nullgen;
@@ -1962,8 +1858,6 @@ yyreduce:
break;
case 38:
-
-/* Line 1806 of yacc.c */
#line 173 "a.y"
{
(yyval.gen2).from = nullgen;
@@ -1972,8 +1866,6 @@ yyreduce:
break;
case 39:
-
-/* Line 1806 of yacc.c */
#line 178 "a.y"
{
(yyval.gen2).from = nullgen;
@@ -1982,8 +1874,6 @@ yyreduce:
break;
case 40:
-
-/* Line 1806 of yacc.c */
#line 183 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
@@ -1992,8 +1882,6 @@ yyreduce:
break;
case 41:
-
-/* Line 1806 of yacc.c */
#line 190 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
@@ -2003,8 +1891,6 @@ yyreduce:
break;
case 42:
-
-/* Line 1806 of yacc.c */
#line 198 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
@@ -2013,8 +1899,6 @@ yyreduce:
break;
case 43:
-
-/* Line 1806 of yacc.c */
#line 203 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
@@ -2024,8 +1908,6 @@ yyreduce:
break;
case 44:
-
-/* Line 1806 of yacc.c */
#line 211 "a.y"
{
(yyval.gen2).from = nullgen;
@@ -2034,8 +1916,6 @@ yyreduce:
break;
case 45:
-
-/* Line 1806 of yacc.c */
#line 216 "a.y"
{
(yyval.gen2).from = nullgen;
@@ -2044,8 +1924,6 @@ yyreduce:
break;
case 46:
-
-/* Line 1806 of yacc.c */
#line 221 "a.y"
{
(yyval.gen2).from = nullgen;
@@ -2056,8 +1934,6 @@ yyreduce:
break;
case 49:
-
-/* Line 1806 of yacc.c */
#line 234 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
@@ -2066,8 +1942,6 @@ yyreduce:
break;
case 50:
-
-/* Line 1806 of yacc.c */
#line 239 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
@@ -2079,8 +1953,6 @@ yyreduce:
break;
case 51:
-
-/* Line 1806 of yacc.c */
#line 249 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
@@ -2089,8 +1961,6 @@ yyreduce:
break;
case 52:
-
-/* Line 1806 of yacc.c */
#line 254 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
@@ -2102,8 +1972,6 @@ yyreduce:
break;
case 53:
-
-/* Line 1806 of yacc.c */
#line 264 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (2)].gen);
@@ -2112,8 +1980,6 @@ yyreduce:
break;
case 54:
-
-/* Line 1806 of yacc.c */
#line 269 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (1)].gen);
@@ -2122,8 +1988,6 @@ yyreduce:
break;
case 55:
-
-/* Line 1806 of yacc.c */
#line 274 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
@@ -2132,8 +1996,6 @@ yyreduce:
break;
case 56:
-
-/* Line 1806 of yacc.c */
#line 281 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
@@ -2142,8 +2004,6 @@ yyreduce:
break;
case 57:
-
-/* Line 1806 of yacc.c */
#line 286 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
@@ -2153,8 +2013,6 @@ yyreduce:
break;
case 58:
-
-/* Line 1806 of yacc.c */
#line 294 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
@@ -2164,8 +2022,6 @@ yyreduce:
break;
case 59:
-
-/* Line 1806 of yacc.c */
#line 302 "a.y"
{
(yyval.gen2).from = (yyvsp[(3) - (5)].gen);
@@ -2177,8 +2033,6 @@ yyreduce:
break;
case 64:
-
-/* Line 1806 of yacc.c */
#line 318 "a.y"
{
(yyval.gen) = (yyvsp[(2) - (2)].gen);
@@ -2186,8 +2040,6 @@ yyreduce:
break;
case 65:
-
-/* Line 1806 of yacc.c */
#line 322 "a.y"
{
(yyval.gen) = (yyvsp[(2) - (2)].gen);
@@ -2195,8 +2047,6 @@ yyreduce:
break;
case 71:
-
-/* Line 1806 of yacc.c */
#line 335 "a.y"
{
(yyval.gen) = nullgen;
@@ -2206,8 +2056,6 @@ yyreduce:
break;
case 72:
-
-/* Line 1806 of yacc.c */
#line 341 "a.y"
{
(yyval.gen) = nullgen;
@@ -2220,8 +2068,6 @@ yyreduce:
break;
case 73:
-
-/* Line 1806 of yacc.c */
#line 350 "a.y"
{
(yyval.gen) = nullgen;
@@ -2232,8 +2078,6 @@ yyreduce:
break;
case 74:
-
-/* Line 1806 of yacc.c */
#line 359 "a.y"
{
(yyval.gen) = nullgen;
@@ -2242,8 +2086,6 @@ yyreduce:
break;
case 75:
-
-/* Line 1806 of yacc.c */
#line 364 "a.y"
{
(yyval.gen) = nullgen;
@@ -2252,8 +2094,6 @@ yyreduce:
break;
case 76:
-
-/* Line 1806 of yacc.c */
#line 369 "a.y"
{
(yyval.gen) = nullgen;
@@ -2262,8 +2102,6 @@ yyreduce:
break;
case 77:
-
-/* Line 1806 of yacc.c */
#line 374 "a.y"
{
(yyval.gen) = nullgen;
@@ -2272,8 +2110,6 @@ yyreduce:
break;
case 78:
-
-/* Line 1806 of yacc.c */
#line 379 "a.y"
{
(yyval.gen) = nullgen;
@@ -2282,8 +2118,6 @@ yyreduce:
break;
case 79:
-
-/* Line 1806 of yacc.c */
#line 384 "a.y"
{
(yyval.gen) = nullgen;
@@ -2292,8 +2126,6 @@ yyreduce:
break;
case 80:
-
-/* Line 1806 of yacc.c */
#line 391 "a.y"
{
(yyval.gen) = nullgen;
@@ -2303,8 +2135,6 @@ yyreduce:
break;
case 81:
-
-/* Line 1806 of yacc.c */
#line 397 "a.y"
{
(yyval.gen) = (yyvsp[(2) - (2)].gen);
@@ -2319,8 +2149,6 @@ yyreduce:
break;
case 82:
-
-/* Line 1806 of yacc.c */
#line 408 "a.y"
{
(yyval.gen) = nullgen;
@@ -2330,8 +2158,6 @@ yyreduce:
break;
case 83:
-
-/* Line 1806 of yacc.c */
#line 414 "a.y"
{
(yyval.gen) = nullgen;
@@ -2341,8 +2167,6 @@ yyreduce:
break;
case 84:
-
-/* Line 1806 of yacc.c */
#line 420 "a.y"
{
(yyval.gen) = nullgen;
@@ -2352,8 +2176,6 @@ yyreduce:
break;
case 85:
-
-/* Line 1806 of yacc.c */
#line 426 "a.y"
{
(yyval.gen) = nullgen;
@@ -2363,8 +2185,6 @@ yyreduce:
break;
case 86:
-
-/* Line 1806 of yacc.c */
#line 432 "a.y"
{
(yyval.gen) = nullgen;
@@ -2374,8 +2194,6 @@ yyreduce:
break;
case 87:
-
-/* Line 1806 of yacc.c */
#line 440 "a.y"
{
(yyval.gen) = nullgen;
@@ -2386,8 +2204,6 @@ yyreduce:
break;
case 88:
-
-/* Line 1806 of yacc.c */
#line 449 "a.y"
{
(yyval.con2).v1 = (yyvsp[(1) - (1)].lval);
@@ -2396,8 +2212,6 @@ yyreduce:
break;
case 89:
-
-/* Line 1806 of yacc.c */
#line 454 "a.y"
{
(yyval.con2).v1 = -(yyvsp[(2) - (2)].lval);
@@ -2406,8 +2220,6 @@ yyreduce:
break;
case 90:
-
-/* Line 1806 of yacc.c */
#line 459 "a.y"
{
(yyval.con2).v1 = (yyvsp[(1) - (3)].lval);
@@ -2416,8 +2228,6 @@ yyreduce:
break;
case 91:
-
-/* Line 1806 of yacc.c */
#line 464 "a.y"
{
(yyval.con2).v1 = -(yyvsp[(2) - (4)].lval);
@@ -2426,8 +2236,6 @@ yyreduce:
break;
case 94:
-
-/* Line 1806 of yacc.c */
#line 475 "a.y"
{
(yyval.gen) = nullgen;
@@ -2437,8 +2245,6 @@ yyreduce:
break;
case 95:
-
-/* Line 1806 of yacc.c */
#line 481 "a.y"
{
(yyval.gen) = nullgen;
@@ -2448,8 +2254,6 @@ yyreduce:
break;
case 96:
-
-/* Line 1806 of yacc.c */
#line 487 "a.y"
{
(yyval.gen) = nullgen;
@@ -2459,8 +2263,6 @@ yyreduce:
break;
case 97:
-
-/* Line 1806 of yacc.c */
#line 493 "a.y"
{
(yyval.gen) = nullgen;
@@ -2473,8 +2275,6 @@ yyreduce:
break;
case 98:
-
-/* Line 1806 of yacc.c */
#line 502 "a.y"
{
(yyval.gen) = nullgen;
@@ -2487,29 +2287,35 @@ yyreduce:
break;
case 99:
-
-/* Line 1806 of yacc.c */
#line 511 "a.y"
{
(yyval.gen) = nullgen;
- (yyval.gen).type = D_INDIR+(yyvsp[(2) - (3)].lval);
+ (yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval);
+ (yyval.gen).offset = (yyvsp[(1) - (9)].lval);
+ (yyval.gen).index = (yyvsp[(6) - (9)].lval);
+ (yyval.gen).scale = (yyvsp[(8) - (9)].lval);
+ checkscale((yyval.gen).scale);
}
break;
case 100:
-
-/* Line 1806 of yacc.c */
-#line 516 "a.y"
+#line 520 "a.y"
{
(yyval.gen) = nullgen;
- (yyval.gen).type = D_INDIR+D_SP;
+ (yyval.gen).type = D_INDIR+(yyvsp[(2) - (3)].lval);
}
break;
case 101:
+#line 525 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+D_SP;
+ }
+ break;
-/* Line 1806 of yacc.c */
-#line 521 "a.y"
+ case 102:
+#line 530 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval);
@@ -2517,10 +2323,8 @@ yyreduce:
}
break;
- case 102:
-
-/* Line 1806 of yacc.c */
-#line 527 "a.y"
+ case 103:
+#line 536 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+D_NONE;
@@ -2530,10 +2334,8 @@ yyreduce:
}
break;
- case 103:
-
-/* Line 1806 of yacc.c */
-#line 535 "a.y"
+ case 104:
+#line 544 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+(yyvsp[(2) - (8)].lval);
@@ -2543,19 +2345,15 @@ yyreduce:
}
break;
- case 104:
-
-/* Line 1806 of yacc.c */
-#line 545 "a.y"
+ case 105:
+#line 554 "a.y"
{
(yyval.gen) = (yyvsp[(1) - (1)].gen);
}
break;
- case 105:
-
-/* Line 1806 of yacc.c */
-#line 549 "a.y"
+ case 106:
+#line 558 "a.y"
{
(yyval.gen) = (yyvsp[(1) - (6)].gen);
(yyval.gen).index = (yyvsp[(3) - (6)].lval);
@@ -2564,10 +2362,8 @@ yyreduce:
}
break;
- case 106:
-
-/* Line 1806 of yacc.c */
-#line 558 "a.y"
+ case 107:
+#line 567 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = (yyvsp[(4) - (5)].lval);
@@ -2576,10 +2372,8 @@ yyreduce:
}
break;
- case 107:
-
-/* Line 1806 of yacc.c */
-#line 565 "a.y"
+ case 108:
+#line 574 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_STATIC;
@@ -2588,194 +2382,144 @@ yyreduce:
}
break;
- case 108:
-
-/* Line 1806 of yacc.c */
-#line 573 "a.y"
+ case 109:
+#line 582 "a.y"
{
(yyval.lval) = 0;
}
break;
- case 109:
-
-/* Line 1806 of yacc.c */
-#line 577 "a.y"
+ case 110:
+#line 586 "a.y"
{
(yyval.lval) = (yyvsp[(2) - (2)].lval);
}
break;
- case 110:
-
-/* Line 1806 of yacc.c */
-#line 581 "a.y"
+ case 111:
+#line 590 "a.y"
{
(yyval.lval) = -(yyvsp[(2) - (2)].lval);
}
break;
- case 112:
-
-/* Line 1806 of yacc.c */
-#line 588 "a.y"
+ case 113:
+#line 597 "a.y"
{
(yyval.lval) = D_AUTO;
}
break;
- case 115:
-
-/* Line 1806 of yacc.c */
-#line 596 "a.y"
+ case 116:
+#line 605 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (1)].sym)->value;
}
break;
- case 116:
-
-/* Line 1806 of yacc.c */
-#line 600 "a.y"
+ case 117:
+#line 609 "a.y"
{
(yyval.lval) = -(yyvsp[(2) - (2)].lval);
}
break;
- case 117:
-
-/* Line 1806 of yacc.c */
-#line 604 "a.y"
+ case 118:
+#line 613 "a.y"
{
(yyval.lval) = (yyvsp[(2) - (2)].lval);
}
break;
- case 118:
-
-/* Line 1806 of yacc.c */
-#line 608 "a.y"
+ case 119:
+#line 617 "a.y"
{
(yyval.lval) = ~(yyvsp[(2) - (2)].lval);
}
break;
- case 119:
-
-/* Line 1806 of yacc.c */
-#line 612 "a.y"
+ case 120:
+#line 621 "a.y"
{
(yyval.lval) = (yyvsp[(2) - (3)].lval);
}
break;
- case 121:
-
-/* Line 1806 of yacc.c */
-#line 619 "a.y"
+ case 122:
+#line 628 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval);
}
break;
- case 122:
-
-/* Line 1806 of yacc.c */
-#line 623 "a.y"
+ case 123:
+#line 632 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval);
}
break;
- case 123:
-
-/* Line 1806 of yacc.c */
-#line 627 "a.y"
+ case 124:
+#line 636 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval);
}
break;
- case 124:
-
-/* Line 1806 of yacc.c */
-#line 631 "a.y"
+ case 125:
+#line 640 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval);
}
break;
- case 125:
-
-/* Line 1806 of yacc.c */
-#line 635 "a.y"
+ case 126:
+#line 644 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval);
}
break;
- case 126:
-
-/* Line 1806 of yacc.c */
-#line 639 "a.y"
+ case 127:
+#line 648 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval);
}
break;
- case 127:
-
-/* Line 1806 of yacc.c */
-#line 643 "a.y"
+ case 128:
+#line 652 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval);
}
break;
- case 128:
-
-/* Line 1806 of yacc.c */
-#line 647 "a.y"
+ case 129:
+#line 656 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval);
}
break;
- case 129:
-
-/* Line 1806 of yacc.c */
-#line 651 "a.y"
+ case 130:
+#line 660 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval);
}
break;
- case 130:
-
-/* Line 1806 of yacc.c */
-#line 655 "a.y"
+ case 131:
+#line 664 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval);
}
break;
-
-/* Line 1806 of yacc.c */
-#line 2766 "y.tab.c"
+/* Line 1267 of yacc.c. */
+#line 2521 "y.tab.c"
default: break;
}
- /* User semantic actions sometimes alter yychar, and that requires
- that yytoken be updated with the new translation. We take the
- approach of translating immediately before every use of yytoken.
- One alternative is translating here after every semantic action,
- but that translation would be missed if the semantic action invokes
- YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
- if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
- incorrect destructor might then be invoked immediately. In the
- case of YYERROR or YYBACKUP, subsequent parser actions might lead
- to an incorrect destructor call or verbose syntax error message
- before the lookahead is translated. */
YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
YYPOPSTACK (yylen);
@@ -2784,6 +2528,7 @@ yyreduce:
*++yyvsp = yyval;
+
/* Now `shift' the result of the reduction. Determine what state
that goes to, based on the state we popped back to and the rule
number reduced by. */
@@ -2803,10 +2548,6 @@ yyreduce:
| yyerrlab -- here on detecting error |
`------------------------------------*/
yyerrlab:
- /* Make sure we have latest lookahead translation. See comments at
- user semantic actions for why this is necessary. */
- yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
-
/* If not already recovering from an error, report this error. */
if (!yyerrstatus)
{
@@ -2814,36 +2555,37 @@ yyerrlab:
#if ! YYERROR_VERBOSE
yyerror (YY_("syntax error"));
#else
-# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
- yyssp, yytoken)
{
- char const *yymsgp = YY_("syntax error");
- int yysyntax_error_status;
- yysyntax_error_status = YYSYNTAX_ERROR;
- if (yysyntax_error_status == 0)
- yymsgp = yymsg;
- else if (yysyntax_error_status == 1)
- {
- if (yymsg != yymsgbuf)
- YYSTACK_FREE (yymsg);
- yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
- if (!yymsg)
- {
- yymsg = yymsgbuf;
- yymsg_alloc = sizeof yymsgbuf;
- yysyntax_error_status = 2;
- }
- else
- {
- yysyntax_error_status = YYSYNTAX_ERROR;
- yymsgp = yymsg;
- }
- }
- yyerror (yymsgp);
- if (yysyntax_error_status == 2)
- goto yyexhaustedlab;
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
}
-# undef YYSYNTAX_ERROR
#endif
}
@@ -2851,7 +2593,7 @@ yyerrlab:
if (yyerrstatus == 3)
{
- /* If just tried and failed to reuse lookahead token after an
+ /* If just tried and failed to reuse look-ahead token after an
error, discard it. */
if (yychar <= YYEOF)
@@ -2868,7 +2610,7 @@ yyerrlab:
}
}
- /* Else will try to reuse lookahead token after shifting the error
+ /* Else will try to reuse look-ahead token after shifting the error
token. */
goto yyerrlab1;
@@ -2902,7 +2644,7 @@ yyerrlab1:
for (;;)
{
yyn = yypact[yystate];
- if (!yypact_value_is_default (yyn))
+ if (yyn != YYPACT_NINF)
{
yyn += YYTERROR;
if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
@@ -2925,6 +2667,9 @@ yyerrlab1:
YY_STACK_PRINT (yyss, yyssp);
}
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
*++yyvsp = yylval;
@@ -2949,7 +2694,7 @@ yyabortlab:
yyresult = 1;
goto yyreturn;
-#if !defined(yyoverflow) || YYERROR_VERBOSE
+#ifndef yyoverflow
/*-------------------------------------------------.
| yyexhaustedlab -- memory exhaustion comes here. |
`-------------------------------------------------*/
@@ -2960,14 +2705,9 @@ yyexhaustedlab:
#endif
yyreturn:
- if (yychar != YYEMPTY)
- {
- /* Make sure we have latest lookahead translation. See comments at
- user semantic actions for why this is necessary. */
- yytoken = YYTRANSLATE (yychar);
- yydestruct ("Cleanup: discarding lookahead",
- yytoken, &yylval);
- }
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
/* Do not reclaim the symbols of the rule which action triggered
this YYABORT or YYACCEPT. */
YYPOPSTACK (yylen);
diff --git a/src/cmd/8a/y.tab.h b/src/cmd/8a/y.tab.h
index 4d7bbe80d..4a84b53e4 100644
--- a/src/cmd/8a/y.tab.h
+++ b/src/cmd/8a/y.tab.h
@@ -1,21 +1,24 @@
-/* A Bison parser, made by GNU Bison 2.5. */
+/* A Bison parser, made by GNU Bison 2.3. */
-/* Bison interface for Yacc-like parsers in C
-
- Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
-
- This program is free software: you can redistribute it and/or modify
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
@@ -26,11 +29,10 @@
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
-
+
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
-
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
@@ -108,11 +110,8 @@
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-{
-
-/* Line 2068 of yacc.c */
#line 37 "a.y"
-
+{
Sym *sym;
int32 lval;
struct {
@@ -123,17 +122,14 @@ typedef union YYSTYPE
char sval[8];
Gen gen;
Gen2 gen2;
-
-
-
-/* Line 2068 of yacc.c */
-#line 131 "y.tab.h"
-} YYSTYPE;
-# define YYSTYPE_IS_TRIVIAL 1
+}
+/* Line 1529 of yacc.c. */
+#line 128 "y.tab.h"
+ YYSTYPE;
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
#endif
extern YYSTYPE yylval;
-
diff --git a/src/cmd/8l/8.out.h b/src/cmd/8l/8.out.h
index 93d44eb51..cf0bc9fee 100644
--- a/src/cmd/8l/8.out.h
+++ b/src/cmd/8l/8.out.h
@@ -534,10 +534,12 @@ enum as
AORPS,
APADDQ,
APAND,
+ APCMPEQB,
APMAXSW,
APMAXUB,
APMINSW,
APMINUB,
+ APMOVMSKB,
APSADBW,
APSUBB,
APSUBL,
@@ -654,6 +656,7 @@ enum
D_PCREL,
D_GOTOFF,
D_GOTREL,
+ D_TLS,
T_TYPE = 1<<0,
T_INDEX = 1<<1,
diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c
index bfeb13839..18591cd2f 100644
--- a/src/cmd/8l/asm.c
+++ b/src/cmd/8l/asm.c
@@ -294,6 +294,12 @@ elfreloc1(Reloc *r, vlong sectoff)
else
return -1;
break;
+
+ case D_TLS:
+ if(r->siz == 4)
+ LPUT(R_386_TLS_LE | elfsym<<8);
+ else
+ return -1;
}
return 0;
diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h
index 980a7f830..ce12d59ba 100644
--- a/src/cmd/8l/l.h
+++ b/src/cmd/8l/l.h
@@ -286,14 +286,12 @@ EXTERN int32 INITTEXT;
EXTERN int32 INITDAT;
EXTERN char* INITENTRY; /* entry point */
EXTERN char* LIBINITENTRY; /* shared library entry point */
-EXTERN int32 casepc;
EXTERN char* pcstr;
EXTERN Auto* curauto;
EXTERN Auto* curhist;
EXTERN Prog* curp;
EXTERN Sym* cursym;
EXTERN Sym* datap;
-EXTERN int32 elfdatsize;
EXTERN int debug[128];
EXTERN char literal[32];
EXTERN Sym* etextp;
@@ -319,7 +317,6 @@ EXTERN int dtype;
EXTERN int tlsoffset;
EXTERN Sym* adrgotype; // type symbol on last Adr read
EXTERN Sym* fromgotype; // type symbol on last p->from read
-EXTERN int elftextsh;
extern Optab optab[];
extern char* anames[];
diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c
index 306e288a3..c819b9936 100644
--- a/src/cmd/8l/obj.c
+++ b/src/cmd/8l/obj.c
@@ -90,7 +90,7 @@ main(int argc, char *argv[])
INITRND = -1;
INITENTRY = 0;
LIBINITENTRY = 0;
- linkmode = LinkInternal; // TODO: LinkAuto once everything works.
+ linkmode = LinkAuto;
nuxiinit();
flagcount("1", "use alternate profiling code", &debug['1']);
@@ -113,6 +113,8 @@ main(int argc, char *argv[])
flagcount("a", "disassemble output", &debug['a']);
flagcount("c", "dump call graph", &debug['c']);
flagcount("d", "disable dynamic executable", &debug['d']);
+ flagstr("extld", "linker to run in external mode", &extld);
+ flagstr("extldflags", "flags for external linker", &extldflags);
flagcount("f", "ignore version mismatch", &debug['f']);
flagcount("g", "disable go package data checks", &debug['g']);
flagfn1("linkmode", "mode: set link mode (internal, external, auto)", setlinkmode);
@@ -139,11 +141,16 @@ main(int argc, char *argv[])
if(HEADTYPE == -1)
HEADTYPE = headtype(goos);
+ // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when
+ // Go was built; see ../../make.bash.
+ if(linkmode == LinkAuto && strcmp(getgoextlinkenabled(), "0") == 0)
+ linkmode = LinkInternal;
+
switch(HEADTYPE) {
default:
if(linkmode == LinkAuto)
linkmode = LinkInternal;
- if(linkmode == LinkExternal)
+ if(linkmode == LinkExternal && strcmp(getgoextlinkenabled(), "1") != 0)
sysfatal("cannot use -linkmode=external with -H %s", headstr(HEADTYPE));
break;
case Hdarwin:
@@ -607,11 +614,15 @@ loop:
goto loop;
case ALOCALS:
+ if(skip)
+ goto casdef;
cursym->locals = p->to.offset;
pc++;
goto loop;
case ATYPE:
+ if(skip)
+ goto casdef;
pc++;
goto loop;
diff --git a/src/cmd/8l/optab.c b/src/cmd/8l/optab.c
index a6ebf063e..1d9d2f55f 100644
--- a/src/cmd/8l/optab.c
+++ b/src/cmd/8l/optab.c
@@ -364,6 +364,12 @@ uchar ysvrs[] =
Ym, Ynone, Zm_o, 2,
0
};
+uchar ymskb[] =
+{
+ Yxr, Yrl, Zm_r_xm, 2,
+ Ymr, Yrl, Zm_r_xm, 1,
+ 0
+};
uchar yxm[] =
{
Yxm, Yxr, Zm_r_xm, 1,
@@ -950,10 +956,12 @@ Optab optab[] =
{ AORPS, yxm, Pm, 0x56 },
{ APADDQ, yxm, Pe, 0xd4 },
{ APAND, yxm, Pe, 0xdb },
+ { APCMPEQB, yxmq, Pe ,0x74 },
{ APMAXSW, yxm, Pe, 0xee },
{ APMAXUB, yxm, Pe, 0xde },
{ APMINSW, yxm, Pe, 0xea },
{ APMINUB, yxm, Pe, 0xda },
+ { APMOVMSKB, ymskb, Px, Pe,0xd7,0xd7 },
{ APSADBW, yxm, Pq, 0xf6 },
{ APSUBB, yxm, Pe, 0xf8 },
{ APSUBL, yxm, Pe, 0xfa },
diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c
index 14dd3e0dc..4871761ff 100644
--- a/src/cmd/8l/pass.c
+++ b/src/cmd/8l/pass.c
@@ -296,16 +296,23 @@ patch(void)
// MOVL 0(GS), reg
// and then off(reg) instead of saying off(GS) directly
// when the offset is negative.
+ // In external mode we just produce a reloc.
if(p->from.type == D_INDIR+D_GS && p->from.offset < 0
&& p->to.type >= D_AX && p->to.type <= D_DI) {
- q = appendp(p);
- q->from = p->from;
- q->from.type = D_INDIR + p->to.type;
- q->to = p->to;
- q->as = p->as;
- p->as = AMOVL;
- p->from.type = D_INDIR+D_GS;
- p->from.offset = 0;
+ if(linkmode != LinkExternal) {
+ q = appendp(p);
+ q->from = p->from;
+ q->from.type = D_INDIR + p->to.type;
+ q->to = p->to;
+ q->as = p->as;
+ p->as = AMOVL;
+ p->from.type = D_INDIR+D_GS;
+ p->from.offset = 0;
+ } else {
+ // Add signals to relocate.
+ p->from.index = D_GS;
+ p->from.scale = 1;
+ }
}
}
if(HEADTYPE == Hplan9x32) {
@@ -450,16 +457,25 @@ dostkoff(void)
break;
case Hlinux:
- p->as = AMOVL;
- p->from.type = D_INDIR+D_GS;
- p->from.offset = 0;
- p->to.type = D_CX;
+ if(linkmode != LinkExternal) {
+ p->as = AMOVL;
+ p->from.type = D_INDIR+D_GS;
+ p->from.offset = 0;
+ p->to.type = D_CX;
- p = appendp(p);
- p->as = AMOVL;
- p->from.type = D_INDIR+D_CX;
- p->from.offset = tlsoffset + 0;
- p->to.type = D_CX;
+ p = appendp(p);
+ p->as = AMOVL;
+ p->from.type = D_INDIR+D_CX;
+ p->from.offset = tlsoffset + 0;
+ p->to.type = D_CX;
+ } else {
+ p->as = AMOVL;
+ p->from.type = D_INDIR+D_GS;
+ p->from.offset = tlsoffset + 0;
+ p->to.type = D_CX;
+ p->from.index = D_GS;
+ p->from.scale = 1;
+ }
break;
case Hplan9x32:
diff --git a/src/cmd/8l/span.c b/src/cmd/8l/span.c
index 0678fa8f2..980186b16 100644
--- a/src/cmd/8l/span.c
+++ b/src/cmd/8l/span.c
@@ -32,6 +32,7 @@
#include "l.h"
#include "../ld/lib.h"
+#include "../ld/elf.h"
static int32 vaddr(Adr*, Reloc*);
@@ -559,6 +560,14 @@ vaddr(Adr *a, Reloc *r)
return v;
}
+static int
+istls(Adr *a)
+{
+ if(HEADTYPE == Hlinux)
+ return a->index == D_GS;
+ return a->type == D_INDIR+D_GS;
+}
+
void
asmand(Adr *a, int r)
{
@@ -569,7 +578,7 @@ asmand(Adr *a, int r)
v = a->offset;
t = a->type;
rel.siz = 0;
- if(a->index != D_NONE) {
+ if(a->index != D_NONE && a->index != D_FS && a->index != D_GS) {
if(t < D_INDIR || t >= 2*D_INDIR) {
switch(t) {
default:
@@ -658,7 +667,7 @@ asmand(Adr *a, int r)
*andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3);
return;
}
- if(v >= -128 && v < 128 && rel.siz == 0) {
+ if(v >= -128 && v < 128 && rel.siz == 0 && a->index != D_FS && a->index != D_GS) {
andptr[0] = (1 << 6) | (reg[t] << 0) | (r << 3);
andptr[1] = v;
andptr += 2;
@@ -680,7 +689,29 @@ putrelv:
r = addrel(cursym);
*r = rel;
r->off = curp->pc + andptr - and;
+ } else if(iself && linkmode == LinkExternal && istls(a) && HEADTYPE != Hopenbsd) {
+ Reloc *r;
+ Sym *s;
+
+ r = addrel(cursym);
+ r->off = curp->pc + andptr - and;
+ r->add = 0;
+ r->xadd = 0;
+ r->siz = 4;
+ r->type = D_TLS;
+ if(a->offset == tlsoffset+0)
+ s = lookup("runtime.g", 0);
+ else
+ s = lookup("runtime.m", 0);
+ s->type = STLSBSS;
+ s->reachable = 1;
+ s->hide = 1;
+ s->size = PtrSize;
+ r->sym = s;
+ r->xsym = s;
+ v = 0;
}
+
put4(v);
return;
diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h
index 866aeb0ab..c8de94120 100644
--- a/src/cmd/cc/cc.h
+++ b/src/cmd/cc/cc.h
@@ -800,6 +800,7 @@ int machcap(Node*);
#pragma varargck type "Q" int32
#pragma varargck type "O" int
#pragma varargck type "O" uint
+#pragma varargck type "S" ushort*
#pragma varargck type "T" Type*
#pragma varargck type "U" char*
#pragma varargck type "|" int
diff --git a/src/cmd/cc/funct.c b/src/cmd/cc/funct.c
index 057151987..7921277b4 100644
--- a/src/cmd/cc/funct.c
+++ b/src/cmd/cc/funct.c
@@ -269,7 +269,7 @@ dclfunct(Type *t, Sym *s)
goto bad;
f = alloc(sizeof(*f));
- for(o=0; o<sizeof(f->sym); o++)
+ for(o=0; o<nelem(f->sym); o++)
f->sym[o] = S;
t->funct = f;
diff --git a/src/cmd/cc/lexbody b/src/cmd/cc/lexbody
index 5fa980267..f4a69739c 100644
--- a/src/cmd/cc/lexbody
+++ b/src/cmd/cc/lexbody
@@ -263,7 +263,7 @@ lookup(void)
for(s = hash[h]; s != S; s = s->link) {
if(s->name[0] != c)
continue;
- if(memcmp(s->name, symb, l) == 0)
+ if(strcmp(s->name, symb) == 0)
return s;
}
s = alloc(sizeof(*s));
diff --git a/src/cmd/dist/a.h b/src/cmd/dist/a.h
index fcd81cc51..73c126476 100644
--- a/src/cmd/dist/a.h
+++ b/src/cmd/dist/a.h
@@ -72,6 +72,7 @@ extern char *gohostos;
extern char *goos;
extern char *goroot;
extern char *goroot_final;
+extern char *goextlinkenabled;
extern char *goversion;
extern char *workdir;
extern char *tooldir;
diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c
index fd6a329c4..e94862325 100644
--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -20,6 +20,7 @@ char *goarm;
char *go386;
char *goroot = GOROOT_FINAL;
char *goroot_final = GOROOT_FINAL;
+char *goextlinkenabled = "";
char *workdir;
char *tooldir;
char *gochar;
@@ -139,6 +140,13 @@ init(void)
bprintf(&b, "%c", gochars[i]);
gochar = btake(&b);
+ xgetenv(&b, "GO_EXTLINK_ENABLED");
+ if(b.len > 0) {
+ goextlinkenabled = btake(&b);
+ if(!streq(goextlinkenabled, "0") && !streq(goextlinkenabled, "1"))
+ fatal("unknown $GO_EXTLINK_ENABLED %s", goextlinkenabled);
+ }
+
xsetenv("GOROOT", goroot);
xsetenv("GOARCH", goarch);
xsetenv("GOOS", goos);
@@ -922,6 +930,8 @@ install(char *dir)
vadd(&compile, bprintf(&b, "GOARM=\"%s\"", goarm));
vadd(&compile, "-D");
vadd(&compile, bprintf(&b, "GO386=\"%s\"", go386));
+ vadd(&compile, "-D");
+ vadd(&compile, bprintf(&b, "GO_EXTLINK_ENABLED=\"%s\"", goextlinkenabled));
}
// gc/lex.c records the GOEXPERIMENT setting used during the build.
diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c
index f46f72d4b..ee867566f 100644
--- a/src/cmd/dist/buildruntime.c
+++ b/src/cmd/dist/buildruntime.c
@@ -133,12 +133,17 @@ static struct {
"// which is where these macros come into play.\n"
"// get_tls sets up the temporary and then g and r use it.\n"
"//\n"
- "// The final wrinkle is that get_tls needs to read from %gs:0,\n"
+ "// Another wrinkle is that get_tls needs to read from %gs:0,\n"
"// but in 8l input it's called 8(GS), because 8l is going to\n"
"// subtract 8 from all the offsets, as described above.\n"
+ "//\n"
+ "// The final wrinkle is that when generating an ELF .o file for\n"
+ "// external linking mode, we need to be able to relocate the\n"
+ "// -8(r) and -4(r) instructions. Tag them with an extra (GS*1)\n"
+ "// that is ignored by the linker except for that identification.\n"
"#define get_tls(r) MOVL 8(GS), r\n"
- "#define g(r) -8(r)\n"
- "#define m(r) -4(r)\n"
+ "#define g(r) -8(r)(GS*1)\n"
+ "#define m(r) -4(r)(GS*1)\n"
},
{"386", "",
"#define get_tls(r)\n"
diff --git a/src/cmd/dist/unix.c b/src/cmd/dist/unix.c
index e4e2dcc9f..f2ea48974 100644
--- a/src/cmd/dist/unix.c
+++ b/src/cmd/dist/unix.c
@@ -698,6 +698,19 @@ main(int argc, char **argv)
if(strcmp(gohostarch, "arm") == 0)
maxnbg = 1;
+ // The OS X 10.6 linker does not support external
+ // linking mode; see
+ // https://code.google.com/p/go/issues/detail?id=5130 .
+ // The mapping from the uname release field to the OS X
+ // version number is complicated, but basically 10 or under is
+ // OS X 10.6 or earlier.
+ if(strcmp(gohostos, "darwin") == 0) {
+ if(uname(&u) < 0)
+ fatal("uname: %s", strerror(errno));
+ if(u.release[1] == '.' || hasprefix(u.release, "10"))
+ goextlinkenabled = "0";
+ }
+
init();
xmain(argc, argv);
bfree(&b);
diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c
index f1d0931d4..9053dfe10 100644
--- a/src/cmd/gc/builtin.c
+++ b/src/cmd/gc/builtin.c
@@ -1,3 +1,4 @@
+// AUTO-GENERATED by mkbuiltin; DO NOT EDIT
char *runtimeimport =
"package runtime\n"
"import runtime \"runtime\"\n"
diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c
index aca30dc1c..c1a9eb14c 100644
--- a/src/cmd/gc/closure.c
+++ b/src/cmd/gc/closure.c
@@ -76,7 +76,7 @@ closurebody(NodeList *body)
return func;
}
-static Node* makeclosure(Node *func, int nowrap);
+static Node* makeclosure(Node *func);
void
typecheckclosure(Node *func, int top)
@@ -125,11 +125,11 @@ typecheckclosure(Node *func, int top)
}
// Create top-level function
- xtop = list(xtop, makeclosure(func, func->cvars==nil || (top&Ecall)));
+ xtop = list(xtop, makeclosure(func));
}
static Node*
-makeclosure(Node *func, int nowrap)
+makeclosure(Node *func)
{
Node *xtype, *v, *addr, *xfunc, *cv;
NodeList *l, *body;
diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c
index add438daf..e9d99df18 100644
--- a/src/cmd/gc/const.c
+++ b/src/cmd/gc/const.c
@@ -1136,7 +1136,6 @@ defaultlit(Node **np, Type *t)
lno = setlineno(n);
ctype = idealkind(n);
- t1 = T;
switch(ctype) {
default:
if(t != T) {
diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c
index 429f212e7..d3759efde 100644
--- a/src/cmd/gc/dcl.c
+++ b/src/cmd/gc/dcl.c
@@ -1339,6 +1339,8 @@ addmethod(Sym *sf, Type *t, int local, int nointerface)
f = methtype(pa, 1);
if(f == T) {
t = pa;
+ if(t == T) // rely on typecheck having complained before
+ return;
if(t != T) {
if(isptr[t->etype]) {
if(t->sym != S) {
@@ -1347,10 +1349,8 @@ addmethod(Sym *sf, Type *t, int local, int nointerface)
}
t = t->type;
}
- }
- if(t->broke) // rely on typecheck having complained before
- return;
- if(t != T) {
+ if(t->broke) // rely on typecheck having complained before
+ return;
if(t->sym == S) {
yyerror("invalid receiver type %T (%T is an unnamed type)", pa, t);
return;
diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c
index ee5b9e939..8a14aa2df 100644
--- a/src/cmd/gc/fmt.c
+++ b/src/cmd/gc/fmt.c
@@ -630,7 +630,7 @@ typefmt(Fmt *fp, Type *t)
case TARRAY:
if(t->bound >= 0)
- return fmtprint(fp, "[%d]%T", (int)t->bound, t->type);
+ return fmtprint(fp, "[%lld]%T", t->bound, t->type);
if(t->bound == -100)
return fmtprint(fp, "[...]%T", t->type);
return fmtprint(fp, "[]%T", t->type);
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index 6be0ec8c9..1f5fb41aa 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -155,7 +155,7 @@ struct Type
Type* orig; // original type (type literal or predefined type)
int lineno;
- // TFUNCT
+ // TFUNC
uchar thistuple;
uchar outtuple;
uchar intuple;
@@ -394,6 +394,7 @@ struct Pkg
uchar imported; // export data of this package was parsed
char exported; // import line written in export data
char direct; // imported directly
+ char safe; // whether the package is marked as safe
};
typedef struct Iter Iter;
diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y
index a12da5671..29cd37008 100644
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -251,7 +251,8 @@ import_package:
} else if(strcmp(importpkg->name, $2->name) != 0)
yyerror("conflicting names %s and %s for package \"%Z\"", importpkg->name, $2->name, importpkg->path);
importpkg->direct = 1;
-
+ importpkg->safe = curio.importsafe;
+
if(safemode && !curio.importsafe)
yyerror("cannot import unsafe package \"%Z\"", importpkg->path);
}
diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c
index 118a0d7a0..b7f71d553 100644
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -602,7 +602,7 @@ void
importfile(Val *f, int line)
{
Biobuf *imp;
- char *file, *p, *q;
+ char *file, *p, *q, *tag;
int32 c;
int len;
Strlit *path;
@@ -610,8 +610,6 @@ importfile(Val *f, int line)
USED(line);
- // TODO(rsc): don't bother reloading imports more than once?
-
if(f->ctype != CTSTR) {
yyerror("import statement not a string");
fakeimport();
@@ -686,7 +684,11 @@ importfile(Val *f, int line)
// to the lexer to avoid parsing export data twice.
if(importpkg->imported) {
file = strdup(namebuf);
- p = smprint("package %s\n$$\n", importpkg->name);
+ tag = "";
+ if(importpkg->safe) {
+ tag = "safe";
+ }
+ p = smprint("package %s %s\n$$\n", importpkg->name, tag);
cannedimports(file, p);
return;
}
diff --git a/src/cmd/gc/mkbuiltin b/src/cmd/gc/mkbuiltin
index 2f76e6f06..1dab1c9a0 100755
--- a/src/cmd/gc/mkbuiltin
+++ b/src/cmd/gc/mkbuiltin
@@ -19,6 +19,7 @@ fi
GC=${GOCHAR}g
gcc -o mkbuiltin1 mkbuiltin1.c
rm -f _builtin.c
+echo "// AUTO-GENERATED by mkbuiltin; DO NOT EDIT" >>_builtin.c
for i in runtime unsafe
do
go tool $GC -A $i.go
diff --git a/src/cmd/gc/racewalk.c b/src/cmd/gc/racewalk.c
index 3e5e592ca..5d4f62e76 100644
--- a/src/cmd/gc/racewalk.c
+++ b/src/cmd/gc/racewalk.c
@@ -26,6 +26,7 @@ static Node* uintptraddr(Node *n);
static Node* basenod(Node *n);
static void foreach(Node *n, void(*f)(Node*, void*), void *c);
static void hascallspred(Node *n, void *c);
+static void appendinit(Node **np, NodeList *init);
static Node* detachexpr(Node *n, NodeList **init);
// Do not instrument the following packages at all,
@@ -132,14 +133,13 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
case OASOP:
case OAS:
case OAS2:
- case OAS2DOTTYPE:
case OAS2RECV:
case OAS2FUNC:
case OAS2MAPR:
racewalknode(&n->left, init, 1, 0);
racewalknode(&n->right, init, 0, 0);
goto ret;
-
+
case OCFUNC:
// can't matter
goto ret;
@@ -185,12 +185,6 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
racewalknode(&n->left, init, 0, 0);
goto ret;
- case OSWITCH:
- if(n->ntest->op == OTYPESW)
- // TODO(dvyukov): the expression can contain calls or reads.
- return;
- goto ret;
-
case ONOT:
case OMINUS:
case OPLUS:
@@ -240,6 +234,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
case OXOR:
case OSUB:
case OMUL:
+ case OHMUL:
case OEQ:
case ONE:
case OLT:
@@ -255,9 +250,13 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
case OANDAND:
case OOROR:
racewalknode(&n->left, init, wr, 0);
- // It requires more complex tree transformation,
- // because we don't know whether it will be executed or not.
- //racewalknode(&n->right, init, wr, 0);
+ // walk has ensured the node has moved to a location where
+ // side effects are safe.
+ // n->right may not be executed,
+ // so instrumentation goes to n->right->ninit, not init.
+ l = nil;
+ racewalknode(&n->right, &l, wr, 0);
+ appendinit(&n->right, l);
goto ret;
case ONAME:
@@ -307,6 +306,14 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
racewalknode(&n->right, init, 0, 0);
goto ret;
+ case OITAB:
+ racewalknode(&n->left, init, 0, 0);
+ goto ret;
+
+ case OTYPESW:
+ racewalknode(&n->right, init, 0, 0);
+ goto ret;
+
// should not appear in AST by now
case OSEND:
case ORECV:
@@ -318,11 +325,13 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
case OPANIC:
case ORECOVER:
case OCONVIFACE:
+ case OCMPIFACE:
case OMAKECHAN:
case OMAKEMAP:
case OMAKESLICE:
case OCALL:
case OCOPY:
+ case OAPPEND:
case ORUNESTR:
case OARRAYBYTESTR:
case OARRAYRUNESTR:
@@ -333,6 +342,13 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
case OADDSTR:
case ODOTTYPE:
case ODOTTYPE2:
+ case OAS2DOTTYPE:
+ case OCALLPART: // lowered to PTRLIT
+ case OCLOSURE: // lowered to PTRLIT
+ case ORANGE: // lowered to ordinary for loop
+ case OARRAYLIT: // lowered to assignments
+ case OMAPLIT:
+ case OSTRUCTLIT:
yyerror("racewalk: %O must be lowered by now", n->op);
goto ret;
@@ -347,6 +363,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
case OIF:
case OCALLMETH:
case ORETURN:
+ case OSWITCH:
case OSELECT:
case OEMPTY:
case OBREAK:
@@ -359,30 +376,18 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
// does not require instrumentation
case OPRINT: // don't bother instrumenting it
case OPRINTN: // don't bother instrumenting it
+ case OCHECKNOTNIL: // always followed by a read.
case OPARAM: // it appears only in fn->exit to copy heap params back
- goto ret;
-
- // unimplemented
- case OSLICESTR:
- case OAPPEND:
- case OCMPIFACE:
- case OARRAYLIT:
- case OMAPLIT:
- case OSTRUCTLIT:
- case OCLOSURE:
- case ODCL:
+ case OCLOSUREVAR:// immutable pointer to captured variable
+ case ODOTMETH: // either part of CALLMETH or CALLPART (lowered to PTRLIT)
+ case OINDREG: // at this stage, only n(SP) nodes from nodarg
+ case ODCL: // declarations (without value) cannot be races
case ODCLCONST:
case ODCLTYPE:
- case OLITERAL:
- case ORANGE:
case OTYPE:
case ONONAME:
- case OINDREG:
- case ODOTMETH:
- case OITAB:
- case OHMUL:
- case OCHECKNOTNIL:
- case OCLOSUREVAR:
+ case OLITERAL:
+ case OSLICESTR: // always preceded by bounds checking, avoid double instrumentation.
goto ret;
}
@@ -398,7 +403,6 @@ ret:
racewalklist(n->nbody, nil);
racewalklist(n->nelse, nil);
racewalklist(n->rlist, nil);
-
*np = n;
}
@@ -440,6 +444,7 @@ callinstr(Node **np, NodeList **init, int wr, int skip)
if(isartificial(n))
return 0;
if(t->etype == TSTRUCT) {
+ // TODO: instrument arrays similarly.
// PARAMs w/o PHEAP are not interesting.
if(n->class == PPARAM || n->class == PPARAMOUT)
return 0;
@@ -476,7 +481,7 @@ callinstr(Node **np, NodeList **init, int wr, int skip)
// that has got a pointer inside. Whether it points to
// the heap or not is impossible to know at compile time
if((class&PHEAP) || class == PPARAMREF || class == PEXTERN
- || b->type->etype == TARRAY || b->op == ODOTPTR || b->op == OIND || b->op == OXDOT) {
+ || b->op == OINDEX || b->op == ODOTPTR || b->op == OIND || b->op == OXDOT) {
hascalls = 0;
foreach(n, hascallspred, &hascalls);
if(hascalls) {
@@ -502,6 +507,8 @@ uintptraddr(Node *n)
return r;
}
+// basenod returns the simplest child node of n pointing to the same
+// memory area.
static Node*
basenod(Node *n)
{
@@ -510,7 +517,7 @@ basenod(Node *n)
n = n->left;
continue;
}
- if(n->op == OINDEX) {
+ if(n->op == OINDEX && isfixedarray(n->type)) {
n = n->left;
continue;
}
@@ -575,3 +582,30 @@ hascallspred(Node *n, void *c)
(*(int*)c)++;
}
}
+
+// appendinit is like addinit in subr.c
+// but appends rather than prepends.
+static void
+appendinit(Node **np, NodeList *init)
+{
+ Node *n;
+
+ if(init == nil)
+ return;
+
+ n = *np;
+ switch(n->op) {
+ case ONAME:
+ case OLITERAL:
+ // There may be multiple refs to this node;
+ // introduce OCONVNOP to hold init list.
+ n = nod(OCONVNOP, n, N);
+ n->type = n->left->type;
+ n->typecheck = 1;
+ *np = n;
+ break;
+ }
+ n->ninit = concat(n->ninit, init);
+ n->ullman = UINF;
+}
+
diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c
index e80a8c723..8af45b9d2 100644
--- a/src/cmd/gc/range.c
+++ b/src/cmd/gc/range.c
@@ -181,9 +181,9 @@ walkrange(Node *n)
case TMAP:
th = typ(TARRAY);
th->type = ptrto(types[TUINT8]);
- // see ../../pkg/runtime/hashmap.h:/hash_iter
+ // see ../../pkg/runtime/hashmap.c:/hash_iter
// Size of hash_iter in # of pointers.
- th->bound = 10;
+ th->bound = 11;
hit = temp(th);
fn = syslook("mapiterinit", 1);
diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c
index 8b546e235..d128bf117 100644
--- a/src/cmd/gc/reflect.c
+++ b/src/cmd/gc/reflect.c
@@ -1069,6 +1069,7 @@ dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size)
case TPTR32:
case TPTR64:
+ // NOTE: Any changes here need to be made to reflect.PtrTo as well.
if(*off % widthptr != 0)
fatal("dgcsym1: invalid alignment, %T", t);
if(!haspointers(t->type) || t->type->etype == TUINT8) {
@@ -1093,6 +1094,7 @@ dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size)
// struct Hchan*
case TCHAN:
+ // NOTE: Any changes here need to be made to reflect.ChanOf as well.
if(*off % widthptr != 0)
fatal("dgcsym1: invalid alignment, %T", t);
ot = duintptr(s, ot, GC_CHAN_PTR);
@@ -1103,6 +1105,7 @@ dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size)
// struct Hmap*
case TMAP:
+ // NOTE: Any changes here need to be made to reflect.MapOf as well.
if(*off % widthptr != 0)
fatal("dgcsym1: invalid alignment, %T", t);
ot = duintptr(s, ot, GC_MAP_PTR);
@@ -1139,6 +1142,7 @@ dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size)
if(t->bound < -1)
fatal("dgcsym1: invalid bound, %T", t);
if(isslice(t)) {
+ // NOTE: Any changes here need to be made to reflect.SliceOf as well.
// struct { byte* array; uint32 len; uint32 cap; }
if(*off % widthptr != 0)
fatal("dgcsym1: invalid alignment, %T", t);
@@ -1152,6 +1156,9 @@ dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size)
}
*off += t->width;
} else {
+ // NOTE: Any changes here need to be made to reflect.ArrayOf as well,
+ // at least once ArrayOf's gc info is implemented and ArrayOf is exported.
+ // struct { byte* array; uint32 len; uint32 cap; }
if(t->bound < 1 || !haspointers(t->type)) {
*off += t->width;
} else if(gcinline(t)) {
diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go
index c49d05c5c..2139a95d9 100644
--- a/src/cmd/gc/runtime.go
+++ b/src/cmd/gc/runtime.go
@@ -89,7 +89,13 @@ func equal(typ *byte, x1, x2 any) (ret bool)
// *byte is really *runtime.Type
func makemap(mapType *byte, hint int64) (hmap map[any]any)
func mapaccess1(mapType *byte, hmap map[any]any, key any) (val any)
+func mapaccess1_fast32(mapType *byte, hmap map[any]any, key any) (val *any)
+func mapaccess1_fast64(mapType *byte, hmap map[any]any, key any) (val *any)
+func mapaccess1_faststr(mapType *byte, hmap map[any]any, key any) (val *any)
func mapaccess2(mapType *byte, hmap map[any]any, key any) (val any, pres bool)
+func mapaccess2_fast32(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
+func mapaccess2_fast64(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
+func mapaccess2_faststr(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
func mapassign1(mapType *byte, hmap map[any]any, key any, val any)
func mapiterinit(mapType *byte, hmap map[any]any, hiter *any)
func mapdelete(mapType *byte, hmap map[any]any, key any)
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index 796851f1a..bd78fb024 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -548,6 +548,12 @@ algtype1(Type *t, Type **bad)
*bad = T;
switch(t->etype) {
+ case TANY:
+ case TFORW:
+ // will be defined later.
+ *bad = t;
+ return -1;
+
case TINT8:
case TUINT8:
case TINT16:
@@ -665,11 +671,14 @@ Type*
maptype(Type *key, Type *val)
{
Type *t;
+ Type *bad;
+ int atype;
if(key != nil) {
- switch(key->etype) {
+ atype = algtype1(key, &bad);
+ switch(bad == T ? key->etype : bad->etype) {
default:
- if(algtype1(key, nil) == ANOEQ)
+ if(atype == ANOEQ)
yyerror("invalid map key type %T", key);
break;
case TANY:
@@ -714,6 +723,12 @@ methcmp(const void *va, const void *vb)
a = *(Type**)va;
b = *(Type**)vb;
+ if(a->sym == S && b->sym == S)
+ return 0;
+ if(a->sym == S)
+ return -1;
+ if(b->sym == S)
+ return 1;
i = strcmp(a->sym->name, b->sym->name);
if(i != 0)
return i;
@@ -1393,7 +1408,7 @@ assignconv(Node *n, Type *t, char *context)
Node *r, *old;
char *why;
- if(n == N || n->type == T)
+ if(n == N || n->type == T || n->type->broke)
return n;
old = n;
@@ -1759,6 +1774,13 @@ ullmancalc(Node *n)
case OCALLINTER:
ul = UINF;
goto out;
+ case OANDAND:
+ case OOROR:
+ // hard with race detector
+ if(flag_race) {
+ ul = UINF;
+ goto out;
+ }
}
ul = 1;
if(n->left != N)
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
index 50e05fc3c..d9c6341b4 100644
--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -2856,11 +2856,19 @@ walkcompare(Node **np, NodeList **init)
typecheck(&call, Etop);
walkstmt(&call);
*init = list(*init, call);
-
- if(n->op == OEQ)
- r = tempbool;
- else
- r = nod(ONOT, tempbool, N);
+
+ // tempbool cannot be used directly as multiple comparison
+ // expressions may exist in the same statement. Create another
+ // temporary to hold the value (its address is not taken so it can
+ // be optimized away).
+ r = temp(types[TBOOL]);
+ a = nod(OAS, r, tempbool);
+ typecheck(&a, Etop);
+ walkstmt(&a);
+ *init = list(*init, a);
+
+ if(n->op != OEQ)
+ r = nod(ONOT, r, N);
typecheck(&r, Erv);
walkexpr(&r, init);
*np = r;
@@ -2889,14 +2897,29 @@ hard:
static int
samecheap(Node *a, Node *b)
{
- if(a == N || b == N || a->op != b->op)
- return 0;
-
- switch(a->op) {
- case ONAME:
- return a == b;
- // TODO: Could do more here, but maybe this is enough.
- // It's all cheapexpr does.
+ Node *ar, *br;
+ while(a != N && b != N && a->op == b->op) {
+ switch(a->op) {
+ default:
+ return 0;
+ case ONAME:
+ return a == b;
+ case ODOT:
+ case ODOTPTR:
+ ar = a->right;
+ br = b->right;
+ if(ar->op != ONAME || br->op != ONAME || ar->sym != br->sym)
+ return 0;
+ break;
+ case OINDEX:
+ ar = a->right;
+ br = b->right;
+ if(!isconst(ar, CTINT) || !isconst(br, CTINT) || mpcmpfixfix(ar->val.u.xval, br->val.u.xval) != 0)
+ return 0;
+ break;
+ }
+ a = a->left;
+ b = b->left;
}
return 0;
}
diff --git a/src/cmd/gc/y.tab.c b/src/cmd/gc/y.tab.c
index dee793998..21c67e805 100644
--- a/src/cmd/gc/y.tab.c
+++ b/src/cmd/gc/y.tab.c
@@ -658,39 +658,39 @@ static const yytype_uint16 yyrline[] =
{
0, 124, 124, 133, 140, 151, 151, 166, 167, 170,
171, 172, 175, 208, 219, 220, 223, 230, 237, 246,
- 259, 260, 267, 267, 280, 284, 285, 289, 294, 300,
- 304, 308, 312, 318, 324, 330, 335, 339, 343, 349,
- 355, 359, 363, 369, 373, 379, 380, 384, 390, 399,
- 405, 423, 428, 440, 456, 461, 468, 488, 506, 515,
- 534, 533, 548, 547, 578, 581, 588, 587, 598, 604,
- 613, 624, 630, 633, 641, 640, 651, 657, 669, 673,
- 678, 668, 699, 698, 711, 714, 720, 723, 735, 739,
- 734, 757, 756, 772, 773, 777, 781, 785, 789, 793,
- 797, 801, 805, 809, 813, 817, 821, 825, 829, 833,
- 837, 841, 845, 850, 856, 857, 861, 872, 876, 880,
- 884, 889, 893, 903, 907, 912, 920, 924, 925, 936,
- 940, 944, 948, 952, 953, 959, 966, 972, 979, 982,
- 989, 995, 1012, 1019, 1020, 1027, 1028, 1047, 1048, 1051,
- 1054, 1058, 1069, 1078, 1084, 1087, 1090, 1097, 1098, 1104,
- 1119, 1127, 1139, 1144, 1150, 1151, 1152, 1153, 1154, 1155,
- 1161, 1162, 1163, 1164, 1170, 1171, 1172, 1173, 1174, 1180,
- 1181, 1184, 1187, 1188, 1189, 1190, 1191, 1194, 1195, 1208,
- 1212, 1217, 1222, 1227, 1231, 1232, 1235, 1241, 1248, 1254,
- 1261, 1267, 1278, 1292, 1321, 1361, 1386, 1404, 1413, 1416,
- 1424, 1428, 1432, 1439, 1445, 1450, 1462, 1465, 1475, 1476,
- 1482, 1483, 1489, 1493, 1499, 1500, 1506, 1510, 1516, 1539,
- 1544, 1550, 1556, 1563, 1572, 1581, 1596, 1602, 1607, 1611,
- 1618, 1631, 1632, 1638, 1644, 1647, 1651, 1657, 1660, 1669,
- 1672, 1673, 1677, 1678, 1684, 1685, 1686, 1687, 1688, 1690,
- 1689, 1704, 1709, 1713, 1717, 1721, 1725, 1730, 1749, 1755,
- 1763, 1767, 1773, 1777, 1783, 1787, 1793, 1797, 1806, 1810,
- 1814, 1818, 1824, 1827, 1835, 1836, 1838, 1839, 1842, 1845,
- 1848, 1851, 1854, 1857, 1860, 1863, 1866, 1869, 1872, 1875,
- 1878, 1881, 1887, 1891, 1895, 1899, 1903, 1907, 1927, 1934,
- 1945, 1946, 1947, 1950, 1951, 1954, 1958, 1968, 1972, 1976,
- 1980, 1984, 1988, 1992, 1998, 2004, 2012, 2020, 2026, 2033,
- 2049, 2067, 2071, 2077, 2080, 2083, 2087, 2097, 2101, 2116,
- 2124, 2125, 2137, 2138, 2141, 2145, 2151, 2155, 2161, 2165
+ 260, 261, 268, 268, 281, 285, 286, 290, 295, 301,
+ 305, 309, 313, 319, 325, 331, 336, 340, 344, 350,
+ 356, 360, 364, 370, 374, 380, 381, 385, 391, 400,
+ 406, 424, 429, 441, 457, 462, 469, 489, 507, 516,
+ 535, 534, 549, 548, 579, 582, 589, 588, 599, 605,
+ 614, 625, 631, 634, 642, 641, 652, 658, 670, 674,
+ 679, 669, 700, 699, 712, 715, 721, 724, 736, 740,
+ 735, 758, 757, 773, 774, 778, 782, 786, 790, 794,
+ 798, 802, 806, 810, 814, 818, 822, 826, 830, 834,
+ 838, 842, 846, 851, 857, 858, 862, 873, 877, 881,
+ 885, 890, 894, 904, 908, 913, 921, 925, 926, 937,
+ 941, 945, 949, 953, 954, 960, 967, 973, 980, 983,
+ 990, 996, 1013, 1020, 1021, 1028, 1029, 1048, 1049, 1052,
+ 1055, 1059, 1070, 1079, 1085, 1088, 1091, 1098, 1099, 1105,
+ 1120, 1128, 1140, 1145, 1151, 1152, 1153, 1154, 1155, 1156,
+ 1162, 1163, 1164, 1165, 1171, 1172, 1173, 1174, 1175, 1181,
+ 1182, 1185, 1188, 1189, 1190, 1191, 1192, 1195, 1196, 1209,
+ 1213, 1218, 1223, 1228, 1232, 1233, 1236, 1242, 1249, 1255,
+ 1262, 1268, 1279, 1293, 1322, 1362, 1387, 1405, 1414, 1417,
+ 1425, 1429, 1433, 1440, 1446, 1451, 1463, 1466, 1476, 1477,
+ 1483, 1484, 1490, 1494, 1500, 1501, 1507, 1511, 1517, 1540,
+ 1545, 1551, 1557, 1564, 1573, 1582, 1597, 1603, 1608, 1612,
+ 1619, 1632, 1633, 1639, 1645, 1648, 1652, 1658, 1661, 1670,
+ 1673, 1674, 1678, 1679, 1685, 1686, 1687, 1688, 1689, 1691,
+ 1690, 1705, 1710, 1714, 1718, 1722, 1726, 1731, 1750, 1756,
+ 1764, 1768, 1774, 1778, 1784, 1788, 1794, 1798, 1807, 1811,
+ 1815, 1819, 1825, 1828, 1836, 1837, 1839, 1840, 1843, 1846,
+ 1849, 1852, 1855, 1858, 1861, 1864, 1867, 1870, 1873, 1876,
+ 1879, 1882, 1888, 1892, 1896, 1900, 1904, 1908, 1928, 1935,
+ 1946, 1947, 1948, 1951, 1952, 1955, 1959, 1969, 1973, 1977,
+ 1981, 1985, 1989, 1993, 1999, 2005, 2013, 2021, 2027, 2034,
+ 2050, 2068, 2072, 2078, 2081, 2084, 2088, 2098, 2102, 2117,
+ 2125, 2126, 2138, 2139, 2142, 2146, 2152, 2156, 2162, 2166
};
#endif
@@ -2494,14 +2494,15 @@ yyreduce:
} else if(strcmp(importpkg->name, (yyvsp[(2) - (4)].sym)->name) != 0)
yyerror("conflicting names %s and %s for package \"%Z\"", importpkg->name, (yyvsp[(2) - (4)].sym)->name, importpkg->path);
importpkg->direct = 1;
-
+ importpkg->safe = curio.importsafe;
+
if(safemode && !curio.importsafe)
yyerror("cannot import unsafe package \"%Z\"", importpkg->path);
}
break;
case 21:
-#line 261 "go.y"
+#line 262 "go.y"
{
if(strcmp((yyvsp[(1) - (1)].sym)->name, "safe") == 0)
curio.importsafe = 1;
@@ -2509,14 +2510,14 @@ yyreduce:
break;
case 22:
-#line 267 "go.y"
+#line 268 "go.y"
{
defercheckwidth();
}
break;
case 23:
-#line 271 "go.y"
+#line 272 "go.y"
{
resumecheckwidth();
unimportfile();
@@ -2524,7 +2525,7 @@ yyreduce:
break;
case 24:
-#line 280 "go.y"
+#line 281 "go.y"
{
yyerror("empty top-level declaration");
(yyval.list) = nil;
@@ -2532,14 +2533,14 @@ yyreduce:
break;
case 26:
-#line 286 "go.y"
+#line 287 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
case 27:
-#line 290 "go.y"
+#line 291 "go.y"
{
yyerror("non-declaration statement outside function body");
(yyval.list) = nil;
@@ -2547,35 +2548,35 @@ yyreduce:
break;
case 28:
-#line 295 "go.y"
+#line 296 "go.y"
{
(yyval.list) = nil;
}
break;
case 29:
-#line 301 "go.y"
+#line 302 "go.y"
{
(yyval.list) = (yyvsp[(2) - (2)].list);
}
break;
case 30:
-#line 305 "go.y"
+#line 306 "go.y"
{
(yyval.list) = (yyvsp[(3) - (5)].list);
}
break;
case 31:
-#line 309 "go.y"
+#line 310 "go.y"
{
(yyval.list) = nil;
}
break;
case 32:
-#line 313 "go.y"
+#line 314 "go.y"
{
(yyval.list) = (yyvsp[(2) - (2)].list);
iota = -100000;
@@ -2584,7 +2585,7 @@ yyreduce:
break;
case 33:
-#line 319 "go.y"
+#line 320 "go.y"
{
(yyval.list) = (yyvsp[(3) - (5)].list);
iota = -100000;
@@ -2593,7 +2594,7 @@ yyreduce:
break;
case 34:
-#line 325 "go.y"
+#line 326 "go.y"
{
(yyval.list) = concat((yyvsp[(3) - (7)].list), (yyvsp[(5) - (7)].list));
iota = -100000;
@@ -2602,7 +2603,7 @@ yyreduce:
break;
case 35:
-#line 331 "go.y"
+#line 332 "go.y"
{
(yyval.list) = nil;
iota = -100000;
@@ -2610,84 +2611,84 @@ yyreduce:
break;
case 36:
-#line 336 "go.y"
+#line 337 "go.y"
{
(yyval.list) = list1((yyvsp[(2) - (2)].node));
}
break;
case 37:
-#line 340 "go.y"
+#line 341 "go.y"
{
(yyval.list) = (yyvsp[(3) - (5)].list);
}
break;
case 38:
-#line 344 "go.y"
+#line 345 "go.y"
{
(yyval.list) = nil;
}
break;
case 39:
-#line 350 "go.y"
+#line 351 "go.y"
{
iota = 0;
}
break;
case 40:
-#line 356 "go.y"
+#line 357 "go.y"
{
(yyval.list) = variter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil);
}
break;
case 41:
-#line 360 "go.y"
+#line 361 "go.y"
{
(yyval.list) = variter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list));
}
break;
case 42:
-#line 364 "go.y"
+#line 365 "go.y"
{
(yyval.list) = variter((yyvsp[(1) - (3)].list), nil, (yyvsp[(3) - (3)].list));
}
break;
case 43:
-#line 370 "go.y"
+#line 371 "go.y"
{
(yyval.list) = constiter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list));
}
break;
case 44:
-#line 374 "go.y"
+#line 375 "go.y"
{
(yyval.list) = constiter((yyvsp[(1) - (3)].list), N, (yyvsp[(3) - (3)].list));
}
break;
case 46:
-#line 381 "go.y"
+#line 382 "go.y"
{
(yyval.list) = constiter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil);
}
break;
case 47:
-#line 385 "go.y"
+#line 386 "go.y"
{
(yyval.list) = constiter((yyvsp[(1) - (1)].list), N, nil);
}
break;
case 48:
-#line 391 "go.y"
+#line 392 "go.y"
{
// different from dclname because the name
// becomes visible right here, not at the end
@@ -2697,14 +2698,14 @@ yyreduce:
break;
case 49:
-#line 400 "go.y"
+#line 401 "go.y"
{
(yyval.node) = typedcl1((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node), 1);
}
break;
case 50:
-#line 406 "go.y"
+#line 407 "go.y"
{
(yyval.node) = (yyvsp[(1) - (1)].node);
@@ -2725,7 +2726,7 @@ yyreduce:
break;
case 51:
-#line 424 "go.y"
+#line 425 "go.y"
{
(yyval.node) = nod(OASOP, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
(yyval.node)->etype = (yyvsp[(2) - (3)].i); // rathole to pass opcode
@@ -2733,7 +2734,7 @@ yyreduce:
break;
case 52:
-#line 429 "go.y"
+#line 430 "go.y"
{
if((yyvsp[(1) - (3)].list)->next == nil && (yyvsp[(3) - (3)].list)->next == nil) {
// simple
@@ -2748,7 +2749,7 @@ yyreduce:
break;
case 53:
-#line 441 "go.y"
+#line 442 "go.y"
{
if((yyvsp[(3) - (3)].list)->n->op == OTYPESW) {
(yyval.node) = nod(OTYPESW, N, (yyvsp[(3) - (3)].list)->n->right);
@@ -2767,7 +2768,7 @@ yyreduce:
break;
case 54:
-#line 457 "go.y"
+#line 458 "go.y"
{
(yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1));
(yyval.node)->etype = OADD;
@@ -2775,7 +2776,7 @@ yyreduce:
break;
case 55:
-#line 462 "go.y"
+#line 463 "go.y"
{
(yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1));
(yyval.node)->etype = OSUB;
@@ -2783,7 +2784,7 @@ yyreduce:
break;
case 56:
-#line 469 "go.y"
+#line 470 "go.y"
{
Node *n, *nn;
@@ -2806,7 +2807,7 @@ yyreduce:
break;
case 57:
-#line 489 "go.y"
+#line 490 "go.y"
{
Node *n;
@@ -2827,7 +2828,7 @@ yyreduce:
break;
case 58:
-#line 507 "go.y"
+#line 508 "go.y"
{
// will be converted to OCASE
// right will point to next case
@@ -2839,7 +2840,7 @@ yyreduce:
break;
case 59:
-#line 516 "go.y"
+#line 517 "go.y"
{
Node *n, *nn;
@@ -2858,14 +2859,14 @@ yyreduce:
break;
case 60:
-#line 534 "go.y"
+#line 535 "go.y"
{
markdcl();
}
break;
case 61:
-#line 538 "go.y"
+#line 539 "go.y"
{
if((yyvsp[(3) - (4)].list) == nil)
(yyval.node) = nod(OEMPTY, N, N);
@@ -2876,7 +2877,7 @@ yyreduce:
break;
case 62:
-#line 548 "go.y"
+#line 549 "go.y"
{
// If the last token read by the lexer was consumed
// as part of the case, clear it (parser has cleared yychar).
@@ -2889,7 +2890,7 @@ yyreduce:
break;
case 63:
-#line 558 "go.y"
+#line 559 "go.y"
{
int last;
@@ -2911,28 +2912,28 @@ yyreduce:
break;
case 64:
-#line 578 "go.y"
+#line 579 "go.y"
{
(yyval.list) = nil;
}
break;
case 65:
-#line 582 "go.y"
+#line 583 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node));
}
break;
case 66:
-#line 588 "go.y"
+#line 589 "go.y"
{
markdcl();
}
break;
case 67:
-#line 592 "go.y"
+#line 593 "go.y"
{
(yyval.list) = (yyvsp[(3) - (4)].list);
popdcl();
@@ -2940,7 +2941,7 @@ yyreduce:
break;
case 68:
-#line 599 "go.y"
+#line 600 "go.y"
{
(yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node));
(yyval.node)->list = (yyvsp[(1) - (4)].list);
@@ -2949,7 +2950,7 @@ yyreduce:
break;
case 69:
-#line 605 "go.y"
+#line 606 "go.y"
{
(yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node));
(yyval.node)->list = (yyvsp[(1) - (4)].list);
@@ -2959,7 +2960,7 @@ yyreduce:
break;
case 70:
-#line 614 "go.y"
+#line 615 "go.y"
{
// init ; test ; incr
if((yyvsp[(5) - (5)].node) != N && (yyvsp[(5) - (5)].node)->colas != 0)
@@ -2973,7 +2974,7 @@ yyreduce:
break;
case 71:
-#line 625 "go.y"
+#line 626 "go.y"
{
// normal test
(yyval.node) = nod(OFOR, N, N);
@@ -2982,7 +2983,7 @@ yyreduce:
break;
case 73:
-#line 634 "go.y"
+#line 635 "go.y"
{
(yyval.node) = (yyvsp[(1) - (2)].node);
(yyval.node)->nbody = concat((yyval.node)->nbody, (yyvsp[(2) - (2)].list));
@@ -2990,14 +2991,14 @@ yyreduce:
break;
case 74:
-#line 641 "go.y"
+#line 642 "go.y"
{
markdcl();
}
break;
case 75:
-#line 645 "go.y"
+#line 646 "go.y"
{
(yyval.node) = (yyvsp[(3) - (3)].node);
popdcl();
@@ -3005,7 +3006,7 @@ yyreduce:
break;
case 76:
-#line 652 "go.y"
+#line 653 "go.y"
{
// test
(yyval.node) = nod(OIF, N, N);
@@ -3014,7 +3015,7 @@ yyreduce:
break;
case 77:
-#line 658 "go.y"
+#line 659 "go.y"
{
// init ; test
(yyval.node) = nod(OIF, N, N);
@@ -3025,14 +3026,14 @@ yyreduce:
break;
case 78:
-#line 669 "go.y"
+#line 670 "go.y"
{
markdcl();
}
break;
case 79:
-#line 673 "go.y"
+#line 674 "go.y"
{
if((yyvsp[(3) - (3)].node)->ntest == N)
yyerror("missing condition in if statement");
@@ -3040,14 +3041,14 @@ yyreduce:
break;
case 80:
-#line 678 "go.y"
+#line 679 "go.y"
{
(yyvsp[(3) - (5)].node)->nbody = (yyvsp[(5) - (5)].list);
}
break;
case 81:
-#line 682 "go.y"
+#line 683 "go.y"
{
Node *n;
NodeList *nn;
@@ -3065,14 +3066,14 @@ yyreduce:
break;
case 82:
-#line 699 "go.y"
+#line 700 "go.y"
{
markdcl();
}
break;
case 83:
-#line 703 "go.y"
+#line 704 "go.y"
{
if((yyvsp[(4) - (5)].node)->ntest == N)
yyerror("missing condition in if statement");
@@ -3082,28 +3083,28 @@ yyreduce:
break;
case 84:
-#line 711 "go.y"
+#line 712 "go.y"
{
(yyval.list) = nil;
}
break;
case 85:
-#line 715 "go.y"
+#line 716 "go.y"
{
(yyval.list) = concat((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].list));
}
break;
case 86:
-#line 720 "go.y"
+#line 721 "go.y"
{
(yyval.list) = nil;
}
break;
case 87:
-#line 724 "go.y"
+#line 725 "go.y"
{
NodeList *node;
@@ -3115,14 +3116,14 @@ yyreduce:
break;
case 88:
-#line 735 "go.y"
+#line 736 "go.y"
{
markdcl();
}
break;
case 89:
-#line 739 "go.y"
+#line 740 "go.y"
{
Node *n;
n = (yyvsp[(3) - (3)].node)->ntest;
@@ -3133,7 +3134,7 @@ yyreduce:
break;
case 90:
-#line 747 "go.y"
+#line 748 "go.y"
{
(yyval.node) = (yyvsp[(3) - (7)].node);
(yyval.node)->op = OSWITCH;
@@ -3144,14 +3145,14 @@ yyreduce:
break;
case 91:
-#line 757 "go.y"
+#line 758 "go.y"
{
typesw = nod(OXXX, typesw, N);
}
break;
case 92:
-#line 761 "go.y"
+#line 762 "go.y"
{
(yyval.node) = nod(OSELECT, N, N);
(yyval.node)->lineno = typesw->lineno;
@@ -3161,154 +3162,154 @@ yyreduce:
break;
case 94:
-#line 774 "go.y"
+#line 775 "go.y"
{
(yyval.node) = nod(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 95:
-#line 778 "go.y"
+#line 779 "go.y"
{
(yyval.node) = nod(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 96:
-#line 782 "go.y"
+#line 783 "go.y"
{
(yyval.node) = nod(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 97:
-#line 786 "go.y"
+#line 787 "go.y"
{
(yyval.node) = nod(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 98:
-#line 790 "go.y"
+#line 791 "go.y"
{
(yyval.node) = nod(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 99:
-#line 794 "go.y"
+#line 795 "go.y"
{
(yyval.node) = nod(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 100:
-#line 798 "go.y"
+#line 799 "go.y"
{
(yyval.node) = nod(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 101:
-#line 802 "go.y"
+#line 803 "go.y"
{
(yyval.node) = nod(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 102:
-#line 806 "go.y"
+#line 807 "go.y"
{
(yyval.node) = nod(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 103:
-#line 810 "go.y"
+#line 811 "go.y"
{
(yyval.node) = nod(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 104:
-#line 814 "go.y"
+#line 815 "go.y"
{
(yyval.node) = nod(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 105:
-#line 818 "go.y"
+#line 819 "go.y"
{
(yyval.node) = nod(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 106:
-#line 822 "go.y"
+#line 823 "go.y"
{
(yyval.node) = nod(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 107:
-#line 826 "go.y"
+#line 827 "go.y"
{
(yyval.node) = nod(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 108:
-#line 830 "go.y"
+#line 831 "go.y"
{
(yyval.node) = nod(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 109:
-#line 834 "go.y"
+#line 835 "go.y"
{
(yyval.node) = nod(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 110:
-#line 838 "go.y"
+#line 839 "go.y"
{
(yyval.node) = nod(OANDNOT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 111:
-#line 842 "go.y"
+#line 843 "go.y"
{
(yyval.node) = nod(OLSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 112:
-#line 846 "go.y"
+#line 847 "go.y"
{
(yyval.node) = nod(ORSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 113:
-#line 851 "go.y"
+#line 852 "go.y"
{
(yyval.node) = nod(OSEND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 115:
-#line 858 "go.y"
+#line 859 "go.y"
{
(yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
}
break;
case 116:
-#line 862 "go.y"
+#line 863 "go.y"
{
if((yyvsp[(2) - (2)].node)->op == OCOMPLIT) {
// Special case for &T{...}: turn into (*T){...}.
@@ -3322,28 +3323,28 @@ yyreduce:
break;
case 117:
-#line 873 "go.y"
+#line 874 "go.y"
{
(yyval.node) = nod(OPLUS, (yyvsp[(2) - (2)].node), N);
}
break;
case 118:
-#line 877 "go.y"
+#line 878 "go.y"
{
(yyval.node) = nod(OMINUS, (yyvsp[(2) - (2)].node), N);
}
break;
case 119:
-#line 881 "go.y"
+#line 882 "go.y"
{
(yyval.node) = nod(ONOT, (yyvsp[(2) - (2)].node), N);
}
break;
case 120:
-#line 885 "go.y"
+#line 886 "go.y"
{
yyerror("the bitwise complement operator is ^");
(yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N);
@@ -3351,28 +3352,28 @@ yyreduce:
break;
case 121:
-#line 890 "go.y"
+#line 891 "go.y"
{
(yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N);
}
break;
case 122:
-#line 894 "go.y"
+#line 895 "go.y"
{
(yyval.node) = nod(ORECV, (yyvsp[(2) - (2)].node), N);
}
break;
case 123:
-#line 904 "go.y"
+#line 905 "go.y"
{
(yyval.node) = nod(OCALL, (yyvsp[(1) - (3)].node), N);
}
break;
case 124:
-#line 908 "go.y"
+#line 909 "go.y"
{
(yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N);
(yyval.node)->list = (yyvsp[(3) - (5)].list);
@@ -3380,7 +3381,7 @@ yyreduce:
break;
case 125:
-#line 913 "go.y"
+#line 914 "go.y"
{
(yyval.node) = nod(OCALL, (yyvsp[(1) - (6)].node), N);
(yyval.node)->list = (yyvsp[(3) - (6)].list);
@@ -3389,14 +3390,14 @@ yyreduce:
break;
case 126:
-#line 921 "go.y"
+#line 922 "go.y"
{
(yyval.node) = nodlit((yyvsp[(1) - (1)].val));
}
break;
case 128:
-#line 926 "go.y"
+#line 927 "go.y"
{
if((yyvsp[(1) - (3)].node)->op == OPACK) {
Sym *s;
@@ -3410,35 +3411,35 @@ yyreduce:
break;
case 129:
-#line 937 "go.y"
+#line 938 "go.y"
{
(yyval.node) = nod(ODOTTYPE, (yyvsp[(1) - (5)].node), (yyvsp[(4) - (5)].node));
}
break;
case 130:
-#line 941 "go.y"
+#line 942 "go.y"
{
(yyval.node) = nod(OTYPESW, N, (yyvsp[(1) - (5)].node));
}
break;
case 131:
-#line 945 "go.y"
+#line 946 "go.y"
{
(yyval.node) = nod(OINDEX, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
}
break;
case 132:
-#line 949 "go.y"
+#line 950 "go.y"
{
(yyval.node) = nod(OSLICE, (yyvsp[(1) - (6)].node), nod(OKEY, (yyvsp[(3) - (6)].node), (yyvsp[(5) - (6)].node)));
}
break;
case 134:
-#line 954 "go.y"
+#line 955 "go.y"
{
// conversion
(yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N);
@@ -3447,7 +3448,7 @@ yyreduce:
break;
case 135:
-#line 960 "go.y"
+#line 961 "go.y"
{
(yyval.node) = (yyvsp[(3) - (5)].node);
(yyval.node)->right = (yyvsp[(1) - (5)].node);
@@ -3457,7 +3458,7 @@ yyreduce:
break;
case 136:
-#line 967 "go.y"
+#line 968 "go.y"
{
(yyval.node) = (yyvsp[(3) - (5)].node);
(yyval.node)->right = (yyvsp[(1) - (5)].node);
@@ -3466,7 +3467,7 @@ yyreduce:
break;
case 137:
-#line 973 "go.y"
+#line 974 "go.y"
{
yyerror("cannot parenthesize type in composite literal");
(yyval.node) = (yyvsp[(5) - (7)].node);
@@ -3476,7 +3477,7 @@ yyreduce:
break;
case 139:
-#line 982 "go.y"
+#line 983 "go.y"
{
// composite expression.
// make node early so we get the right line number.
@@ -3485,14 +3486,14 @@ yyreduce:
break;
case 140:
-#line 990 "go.y"
+#line 991 "go.y"
{
(yyval.node) = nod(OKEY, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 141:
-#line 996 "go.y"
+#line 997 "go.y"
{
// These nodes do not carry line numbers.
// Since a composite literal commonly spans several lines,
@@ -3512,7 +3513,7 @@ yyreduce:
break;
case 142:
-#line 1013 "go.y"
+#line 1014 "go.y"
{
(yyval.node) = (yyvsp[(2) - (4)].node);
(yyval.node)->list = (yyvsp[(3) - (4)].list);
@@ -3520,7 +3521,7 @@ yyreduce:
break;
case 144:
-#line 1021 "go.y"
+#line 1022 "go.y"
{
(yyval.node) = (yyvsp[(2) - (4)].node);
(yyval.node)->list = (yyvsp[(3) - (4)].list);
@@ -3528,7 +3529,7 @@ yyreduce:
break;
case 146:
-#line 1029 "go.y"
+#line 1030 "go.y"
{
(yyval.node) = (yyvsp[(2) - (3)].node);
@@ -3548,21 +3549,21 @@ yyreduce:
break;
case 150:
-#line 1055 "go.y"
+#line 1056 "go.y"
{
(yyval.i) = LBODY;
}
break;
case 151:
-#line 1059 "go.y"
+#line 1060 "go.y"
{
(yyval.i) = '{';
}
break;
case 152:
-#line 1070 "go.y"
+#line 1071 "go.y"
{
if((yyvsp[(1) - (1)].sym) == S)
(yyval.node) = N;
@@ -3572,21 +3573,21 @@ yyreduce:
break;
case 153:
-#line 1079 "go.y"
+#line 1080 "go.y"
{
(yyval.node) = dclname((yyvsp[(1) - (1)].sym));
}
break;
case 154:
-#line 1084 "go.y"
+#line 1085 "go.y"
{
(yyval.node) = N;
}
break;
case 156:
-#line 1091 "go.y"
+#line 1092 "go.y"
{
(yyval.sym) = (yyvsp[(1) - (1)].sym);
// during imports, unqualified non-exported identifiers are from builtinpkg
@@ -3596,14 +3597,14 @@ yyreduce:
break;
case 158:
-#line 1099 "go.y"
+#line 1100 "go.y"
{
(yyval.sym) = S;
}
break;
case 159:
-#line 1105 "go.y"
+#line 1106 "go.y"
{
Pkg *p;
@@ -3619,7 +3620,7 @@ yyreduce:
break;
case 160:
-#line 1120 "go.y"
+#line 1121 "go.y"
{
(yyval.node) = oldname((yyvsp[(1) - (1)].sym));
if((yyval.node)->pack != N)
@@ -3628,7 +3629,7 @@ yyreduce:
break;
case 162:
-#line 1140 "go.y"
+#line 1141 "go.y"
{
yyerror("final argument in variadic function missing type");
(yyval.node) = nod(ODDD, typenod(typ(TINTER)), N);
@@ -3636,35 +3637,35 @@ yyreduce:
break;
case 163:
-#line 1145 "go.y"
+#line 1146 "go.y"
{
(yyval.node) = nod(ODDD, (yyvsp[(2) - (2)].node), N);
}
break;
case 169:
-#line 1156 "go.y"
+#line 1157 "go.y"
{
(yyval.node) = nod(OTPAREN, (yyvsp[(2) - (3)].node), N);
}
break;
case 173:
-#line 1165 "go.y"
+#line 1166 "go.y"
{
(yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
}
break;
case 178:
-#line 1175 "go.y"
+#line 1176 "go.y"
{
(yyval.node) = nod(OTPAREN, (yyvsp[(2) - (3)].node), N);
}
break;
case 188:
-#line 1196 "go.y"
+#line 1197 "go.y"
{
if((yyvsp[(1) - (3)].node)->op == OPACK) {
Sym *s;
@@ -3678,14 +3679,14 @@ yyreduce:
break;
case 189:
-#line 1209 "go.y"
+#line 1210 "go.y"
{
(yyval.node) = nod(OTARRAY, (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].node));
}
break;
case 190:
-#line 1213 "go.y"
+#line 1214 "go.y"
{
// array literal of nelem
(yyval.node) = nod(OTARRAY, nod(ODDD, N, N), (yyvsp[(4) - (4)].node));
@@ -3693,7 +3694,7 @@ yyreduce:
break;
case 191:
-#line 1218 "go.y"
+#line 1219 "go.y"
{
(yyval.node) = nod(OTCHAN, (yyvsp[(2) - (2)].node), N);
(yyval.node)->etype = Cboth;
@@ -3701,7 +3702,7 @@ yyreduce:
break;
case 192:
-#line 1223 "go.y"
+#line 1224 "go.y"
{
(yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N);
(yyval.node)->etype = Csend;
@@ -3709,21 +3710,21 @@ yyreduce:
break;
case 193:
-#line 1228 "go.y"
+#line 1229 "go.y"
{
(yyval.node) = nod(OTMAP, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node));
}
break;
case 196:
-#line 1236 "go.y"
+#line 1237 "go.y"
{
(yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
}
break;
case 197:
-#line 1242 "go.y"
+#line 1243 "go.y"
{
(yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N);
(yyval.node)->etype = Crecv;
@@ -3731,7 +3732,7 @@ yyreduce:
break;
case 198:
-#line 1249 "go.y"
+#line 1250 "go.y"
{
(yyval.node) = nod(OTSTRUCT, N, N);
(yyval.node)->list = (yyvsp[(3) - (5)].list);
@@ -3740,7 +3741,7 @@ yyreduce:
break;
case 199:
-#line 1255 "go.y"
+#line 1256 "go.y"
{
(yyval.node) = nod(OTSTRUCT, N, N);
fixlbrace((yyvsp[(2) - (3)].i));
@@ -3748,7 +3749,7 @@ yyreduce:
break;
case 200:
-#line 1262 "go.y"
+#line 1263 "go.y"
{
(yyval.node) = nod(OTINTER, N, N);
(yyval.node)->list = (yyvsp[(3) - (5)].list);
@@ -3757,7 +3758,7 @@ yyreduce:
break;
case 201:
-#line 1268 "go.y"
+#line 1269 "go.y"
{
(yyval.node) = nod(OTINTER, N, N);
fixlbrace((yyvsp[(2) - (3)].i));
@@ -3765,7 +3766,7 @@ yyreduce:
break;
case 202:
-#line 1279 "go.y"
+#line 1280 "go.y"
{
(yyval.node) = (yyvsp[(2) - (3)].node);
if((yyval.node) == N)
@@ -3780,7 +3781,7 @@ yyreduce:
break;
case 203:
-#line 1293 "go.y"
+#line 1294 "go.y"
{
Node *t;
@@ -3812,7 +3813,7 @@ yyreduce:
break;
case 204:
-#line 1322 "go.y"
+#line 1323 "go.y"
{
Node *rcvr, *t;
@@ -3853,7 +3854,7 @@ yyreduce:
break;
case 205:
-#line 1362 "go.y"
+#line 1363 "go.y"
{
Sym *s;
Type *t;
@@ -3881,7 +3882,7 @@ yyreduce:
break;
case 206:
-#line 1387 "go.y"
+#line 1388 "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));
@@ -3900,7 +3901,7 @@ yyreduce:
break;
case 207:
-#line 1405 "go.y"
+#line 1406 "go.y"
{
(yyvsp[(3) - (5)].list) = checkarglist((yyvsp[(3) - (5)].list), 1);
(yyval.node) = nod(OTFUNC, N, N);
@@ -3910,14 +3911,14 @@ yyreduce:
break;
case 208:
-#line 1413 "go.y"
+#line 1414 "go.y"
{
(yyval.list) = nil;
}
break;
case 209:
-#line 1417 "go.y"
+#line 1418 "go.y"
{
(yyval.list) = (yyvsp[(2) - (3)].list);
if((yyval.list) == nil)
@@ -3926,21 +3927,21 @@ yyreduce:
break;
case 210:
-#line 1425 "go.y"
+#line 1426 "go.y"
{
(yyval.list) = nil;
}
break;
case 211:
-#line 1429 "go.y"
+#line 1430 "go.y"
{
(yyval.list) = list1(nod(ODCLFIELD, N, (yyvsp[(1) - (1)].node)));
}
break;
case 212:
-#line 1433 "go.y"
+#line 1434 "go.y"
{
(yyvsp[(2) - (3)].list) = checkarglist((yyvsp[(2) - (3)].list), 0);
(yyval.list) = (yyvsp[(2) - (3)].list);
@@ -3948,14 +3949,14 @@ yyreduce:
break;
case 213:
-#line 1440 "go.y"
+#line 1441 "go.y"
{
closurehdr((yyvsp[(1) - (1)].node));
}
break;
case 214:
-#line 1446 "go.y"
+#line 1447 "go.y"
{
(yyval.node) = closurebody((yyvsp[(3) - (4)].list));
fixlbrace((yyvsp[(2) - (4)].i));
@@ -3963,21 +3964,21 @@ yyreduce:
break;
case 215:
-#line 1451 "go.y"
+#line 1452 "go.y"
{
(yyval.node) = closurebody(nil);
}
break;
case 216:
-#line 1462 "go.y"
+#line 1463 "go.y"
{
(yyval.list) = nil;
}
break;
case 217:
-#line 1466 "go.y"
+#line 1467 "go.y"
{
(yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(2) - (3)].list));
if(nsyntaxerrors == 0)
@@ -3988,56 +3989,56 @@ yyreduce:
break;
case 219:
-#line 1477 "go.y"
+#line 1478 "go.y"
{
(yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
}
break;
case 221:
-#line 1484 "go.y"
+#line 1485 "go.y"
{
(yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
}
break;
case 222:
-#line 1490 "go.y"
+#line 1491 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
case 223:
-#line 1494 "go.y"
+#line 1495 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
case 225:
-#line 1501 "go.y"
+#line 1502 "go.y"
{
(yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
}
break;
case 226:
-#line 1507 "go.y"
+#line 1508 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
case 227:
-#line 1511 "go.y"
+#line 1512 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
case 228:
-#line 1517 "go.y"
+#line 1518 "go.y"
{
NodeList *l;
@@ -4063,7 +4064,7 @@ yyreduce:
break;
case 229:
-#line 1540 "go.y"
+#line 1541 "go.y"
{
(yyvsp[(1) - (2)].node)->val = (yyvsp[(2) - (2)].val);
(yyval.list) = list1((yyvsp[(1) - (2)].node));
@@ -4071,7 +4072,7 @@ yyreduce:
break;
case 230:
-#line 1545 "go.y"
+#line 1546 "go.y"
{
(yyvsp[(2) - (4)].node)->val = (yyvsp[(4) - (4)].val);
(yyval.list) = list1((yyvsp[(2) - (4)].node));
@@ -4080,7 +4081,7 @@ yyreduce:
break;
case 231:
-#line 1551 "go.y"
+#line 1552 "go.y"
{
(yyvsp[(2) - (3)].node)->right = nod(OIND, (yyvsp[(2) - (3)].node)->right, N);
(yyvsp[(2) - (3)].node)->val = (yyvsp[(3) - (3)].val);
@@ -4089,7 +4090,7 @@ yyreduce:
break;
case 232:
-#line 1557 "go.y"
+#line 1558 "go.y"
{
(yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N);
(yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val);
@@ -4099,7 +4100,7 @@ yyreduce:
break;
case 233:
-#line 1564 "go.y"
+#line 1565 "go.y"
{
(yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N);
(yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val);
@@ -4109,7 +4110,7 @@ yyreduce:
break;
case 234:
-#line 1573 "go.y"
+#line 1574 "go.y"
{
Node *n;
@@ -4121,7 +4122,7 @@ yyreduce:
break;
case 235:
-#line 1582 "go.y"
+#line 1583 "go.y"
{
Pkg *pkg;
@@ -4137,14 +4138,14 @@ yyreduce:
break;
case 236:
-#line 1597 "go.y"
+#line 1598 "go.y"
{
(yyval.node) = embedded((yyvsp[(1) - (1)].sym));
}
break;
case 237:
-#line 1603 "go.y"
+#line 1604 "go.y"
{
(yyval.node) = nod(ODCLFIELD, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
ifacedcl((yyval.node));
@@ -4152,14 +4153,14 @@ yyreduce:
break;
case 238:
-#line 1608 "go.y"
+#line 1609 "go.y"
{
(yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(1) - (1)].sym)));
}
break;
case 239:
-#line 1612 "go.y"
+#line 1613 "go.y"
{
(yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(2) - (3)].sym)));
yyerror("cannot parenthesize embedded type");
@@ -4167,7 +4168,7 @@ yyreduce:
break;
case 240:
-#line 1619 "go.y"
+#line 1620 "go.y"
{
// without func keyword
(yyvsp[(2) - (4)].list) = checkarglist((yyvsp[(2) - (4)].list), 1);
@@ -4178,7 +4179,7 @@ yyreduce:
break;
case 242:
-#line 1633 "go.y"
+#line 1634 "go.y"
{
(yyval.node) = nod(ONONAME, N, N);
(yyval.node)->sym = (yyvsp[(1) - (2)].sym);
@@ -4187,7 +4188,7 @@ yyreduce:
break;
case 243:
-#line 1639 "go.y"
+#line 1640 "go.y"
{
(yyval.node) = nod(ONONAME, N, N);
(yyval.node)->sym = (yyvsp[(1) - (2)].sym);
@@ -4196,56 +4197,56 @@ yyreduce:
break;
case 245:
-#line 1648 "go.y"
+#line 1649 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
case 246:
-#line 1652 "go.y"
+#line 1653 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
case 247:
-#line 1657 "go.y"
+#line 1658 "go.y"
{
(yyval.list) = nil;
}
break;
case 248:
-#line 1661 "go.y"
+#line 1662 "go.y"
{
(yyval.list) = (yyvsp[(1) - (2)].list);
}
break;
case 249:
-#line 1669 "go.y"
+#line 1670 "go.y"
{
(yyval.node) = N;
}
break;
case 251:
-#line 1674 "go.y"
+#line 1675 "go.y"
{
(yyval.node) = liststmt((yyvsp[(1) - (1)].list));
}
break;
case 253:
-#line 1679 "go.y"
+#line 1680 "go.y"
{
(yyval.node) = N;
}
break;
case 259:
-#line 1690 "go.y"
+#line 1691 "go.y"
{
(yyvsp[(1) - (2)].node) = nod(OLABEL, (yyvsp[(1) - (2)].node), N);
(yyvsp[(1) - (2)].node)->sym = dclstack; // context, for goto restrictions
@@ -4253,7 +4254,7 @@ yyreduce:
break;
case 260:
-#line 1695 "go.y"
+#line 1696 "go.y"
{
NodeList *l;
@@ -4266,7 +4267,7 @@ yyreduce:
break;
case 261:
-#line 1705 "go.y"
+#line 1706 "go.y"
{
// will be converted to OFALL
(yyval.node) = nod(OXFALL, N, N);
@@ -4274,35 +4275,35 @@ yyreduce:
break;
case 262:
-#line 1710 "go.y"
+#line 1711 "go.y"
{
(yyval.node) = nod(OBREAK, (yyvsp[(2) - (2)].node), N);
}
break;
case 263:
-#line 1714 "go.y"
+#line 1715 "go.y"
{
(yyval.node) = nod(OCONTINUE, (yyvsp[(2) - (2)].node), N);
}
break;
case 264:
-#line 1718 "go.y"
+#line 1719 "go.y"
{
(yyval.node) = nod(OPROC, (yyvsp[(2) - (2)].node), N);
}
break;
case 265:
-#line 1722 "go.y"
+#line 1723 "go.y"
{
(yyval.node) = nod(ODEFER, (yyvsp[(2) - (2)].node), N);
}
break;
case 266:
-#line 1726 "go.y"
+#line 1727 "go.y"
{
(yyval.node) = nod(OGOTO, (yyvsp[(2) - (2)].node), N);
(yyval.node)->sym = dclstack; // context, for goto restrictions
@@ -4310,7 +4311,7 @@ yyreduce:
break;
case 267:
-#line 1731 "go.y"
+#line 1732 "go.y"
{
(yyval.node) = nod(ORETURN, N, N);
(yyval.node)->list = (yyvsp[(2) - (2)].list);
@@ -4330,7 +4331,7 @@ yyreduce:
break;
case 268:
-#line 1750 "go.y"
+#line 1751 "go.y"
{
(yyval.list) = nil;
if((yyvsp[(1) - (1)].node) != N)
@@ -4339,7 +4340,7 @@ yyreduce:
break;
case 269:
-#line 1756 "go.y"
+#line 1757 "go.y"
{
(yyval.list) = (yyvsp[(1) - (3)].list);
if((yyvsp[(3) - (3)].node) != N)
@@ -4348,189 +4349,189 @@ yyreduce:
break;
case 270:
-#line 1764 "go.y"
+#line 1765 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
case 271:
-#line 1768 "go.y"
+#line 1769 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
case 272:
-#line 1774 "go.y"
+#line 1775 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
case 273:
-#line 1778 "go.y"
+#line 1779 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
case 274:
-#line 1784 "go.y"
+#line 1785 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
case 275:
-#line 1788 "go.y"
+#line 1789 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
case 276:
-#line 1794 "go.y"
+#line 1795 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
case 277:
-#line 1798 "go.y"
+#line 1799 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
case 278:
-#line 1807 "go.y"
+#line 1808 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
case 279:
-#line 1811 "go.y"
+#line 1812 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
case 280:
-#line 1815 "go.y"
+#line 1816 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
case 281:
-#line 1819 "go.y"
+#line 1820 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
case 282:
-#line 1824 "go.y"
+#line 1825 "go.y"
{
(yyval.list) = nil;
}
break;
case 283:
-#line 1828 "go.y"
+#line 1829 "go.y"
{
(yyval.list) = (yyvsp[(1) - (2)].list);
}
break;
case 288:
-#line 1842 "go.y"
+#line 1843 "go.y"
{
(yyval.node) = N;
}
break;
case 290:
-#line 1848 "go.y"
+#line 1849 "go.y"
{
(yyval.list) = nil;
}
break;
case 292:
-#line 1854 "go.y"
+#line 1855 "go.y"
{
(yyval.node) = N;
}
break;
case 294:
-#line 1860 "go.y"
+#line 1861 "go.y"
{
(yyval.list) = nil;
}
break;
case 296:
-#line 1866 "go.y"
+#line 1867 "go.y"
{
(yyval.list) = nil;
}
break;
case 298:
-#line 1872 "go.y"
+#line 1873 "go.y"
{
(yyval.list) = nil;
}
break;
case 300:
-#line 1878 "go.y"
+#line 1879 "go.y"
{
(yyval.val).ctype = CTxxx;
}
break;
case 302:
-#line 1888 "go.y"
+#line 1889 "go.y"
{
importimport((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].val).u.sval);
}
break;
case 303:
-#line 1892 "go.y"
+#line 1893 "go.y"
{
importvar((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].type));
}
break;
case 304:
-#line 1896 "go.y"
+#line 1897 "go.y"
{
importconst((yyvsp[(2) - (5)].sym), types[TIDEAL], (yyvsp[(4) - (5)].node));
}
break;
case 305:
-#line 1900 "go.y"
+#line 1901 "go.y"
{
importconst((yyvsp[(2) - (6)].sym), (yyvsp[(3) - (6)].type), (yyvsp[(5) - (6)].node));
}
break;
case 306:
-#line 1904 "go.y"
+#line 1905 "go.y"
{
importtype((yyvsp[(2) - (4)].type), (yyvsp[(3) - (4)].type));
}
break;
case 307:
-#line 1908 "go.y"
+#line 1909 "go.y"
{
if((yyvsp[(2) - (4)].node) == N) {
dclcontext = PEXTERN; // since we skip the funcbody below
@@ -4551,7 +4552,7 @@ yyreduce:
break;
case 308:
-#line 1928 "go.y"
+#line 1929 "go.y"
{
(yyval.sym) = (yyvsp[(1) - (1)].sym);
structpkg = (yyval.sym)->pkg;
@@ -4559,7 +4560,7 @@ yyreduce:
break;
case 309:
-#line 1935 "go.y"
+#line 1936 "go.y"
{
(yyval.type) = pkgtype((yyvsp[(1) - (1)].sym));
importsym((yyvsp[(1) - (1)].sym), OTYPE);
@@ -4567,14 +4568,14 @@ yyreduce:
break;
case 315:
-#line 1955 "go.y"
+#line 1956 "go.y"
{
(yyval.type) = pkgtype((yyvsp[(1) - (1)].sym));
}
break;
case 316:
-#line 1959 "go.y"
+#line 1960 "go.y"
{
// predefined name like uint8
(yyvsp[(1) - (1)].sym) = pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg);
@@ -4587,49 +4588,49 @@ yyreduce:
break;
case 317:
-#line 1969 "go.y"
+#line 1970 "go.y"
{
(yyval.type) = aindex(N, (yyvsp[(3) - (3)].type));
}
break;
case 318:
-#line 1973 "go.y"
+#line 1974 "go.y"
{
(yyval.type) = aindex(nodlit((yyvsp[(2) - (4)].val)), (yyvsp[(4) - (4)].type));
}
break;
case 319:
-#line 1977 "go.y"
+#line 1978 "go.y"
{
(yyval.type) = maptype((yyvsp[(3) - (5)].type), (yyvsp[(5) - (5)].type));
}
break;
case 320:
-#line 1981 "go.y"
+#line 1982 "go.y"
{
(yyval.type) = tostruct((yyvsp[(3) - (4)].list));
}
break;
case 321:
-#line 1985 "go.y"
+#line 1986 "go.y"
{
(yyval.type) = tointerface((yyvsp[(3) - (4)].list));
}
break;
case 322:
-#line 1989 "go.y"
+#line 1990 "go.y"
{
(yyval.type) = ptrto((yyvsp[(2) - (2)].type));
}
break;
case 323:
-#line 1993 "go.y"
+#line 1994 "go.y"
{
(yyval.type) = typ(TCHAN);
(yyval.type)->type = (yyvsp[(2) - (2)].type);
@@ -4638,7 +4639,7 @@ yyreduce:
break;
case 324:
-#line 1999 "go.y"
+#line 2000 "go.y"
{
(yyval.type) = typ(TCHAN);
(yyval.type)->type = (yyvsp[(3) - (4)].type);
@@ -4647,7 +4648,7 @@ yyreduce:
break;
case 325:
-#line 2005 "go.y"
+#line 2006 "go.y"
{
(yyval.type) = typ(TCHAN);
(yyval.type)->type = (yyvsp[(3) - (3)].type);
@@ -4656,7 +4657,7 @@ yyreduce:
break;
case 326:
-#line 2013 "go.y"
+#line 2014 "go.y"
{
(yyval.type) = typ(TCHAN);
(yyval.type)->type = (yyvsp[(3) - (3)].type);
@@ -4665,14 +4666,14 @@ yyreduce:
break;
case 327:
-#line 2021 "go.y"
+#line 2022 "go.y"
{
(yyval.type) = functype(nil, (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list));
}
break;
case 328:
-#line 2027 "go.y"
+#line 2028 "go.y"
{
(yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(2) - (3)].type)));
if((yyvsp[(1) - (3)].sym))
@@ -4682,7 +4683,7 @@ yyreduce:
break;
case 329:
-#line 2034 "go.y"
+#line 2035 "go.y"
{
Type *t;
@@ -4699,7 +4700,7 @@ yyreduce:
break;
case 330:
-#line 2050 "go.y"
+#line 2051 "go.y"
{
Sym *s;
@@ -4718,49 +4719,49 @@ yyreduce:
break;
case 331:
-#line 2068 "go.y"
+#line 2069 "go.y"
{
(yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (5)].sym)), typenod(functype(fakethis(), (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list))));
}
break;
case 332:
-#line 2072 "go.y"
+#line 2073 "go.y"
{
(yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type)));
}
break;
case 333:
-#line 2077 "go.y"
+#line 2078 "go.y"
{
(yyval.list) = nil;
}
break;
case 335:
-#line 2084 "go.y"
+#line 2085 "go.y"
{
(yyval.list) = (yyvsp[(2) - (3)].list);
}
break;
case 336:
-#line 2088 "go.y"
+#line 2089 "go.y"
{
(yyval.list) = list1(nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type))));
}
break;
case 337:
-#line 2098 "go.y"
+#line 2099 "go.y"
{
(yyval.node) = nodlit((yyvsp[(1) - (1)].val));
}
break;
case 338:
-#line 2102 "go.y"
+#line 2103 "go.y"
{
(yyval.node) = nodlit((yyvsp[(2) - (2)].val));
switch((yyval.node)->val.ctype){
@@ -4778,7 +4779,7 @@ yyreduce:
break;
case 339:
-#line 2117 "go.y"
+#line 2118 "go.y"
{
(yyval.node) = oldname(pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg));
if((yyval.node)->op != OLITERAL)
@@ -4787,7 +4788,7 @@ yyreduce:
break;
case 341:
-#line 2126 "go.y"
+#line 2127 "go.y"
{
if((yyvsp[(2) - (5)].node)->val.ctype == CTRUNE && (yyvsp[(4) - (5)].node)->val.ctype == CTINT) {
(yyval.node) = (yyvsp[(2) - (5)].node);
@@ -4801,42 +4802,42 @@ yyreduce:
break;
case 344:
-#line 2142 "go.y"
+#line 2143 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
case 345:
-#line 2146 "go.y"
+#line 2147 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
case 346:
-#line 2152 "go.y"
+#line 2153 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
case 347:
-#line 2156 "go.y"
+#line 2157 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
case 348:
-#line 2162 "go.y"
+#line 2163 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
case 349:
-#line 2166 "go.y"
+#line 2167 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
@@ -4844,7 +4845,7 @@ yyreduce:
/* Line 1267 of yacc.c. */
-#line 4849 "y.tab.c"
+#line 4850 "y.tab.c"
default: break;
}
YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
@@ -5058,7 +5059,7 @@ yyreturn:
}
-#line 2170 "go.y"
+#line 2171 "go.y"
static void
diff --git a/src/cmd/go/clean.go b/src/cmd/go/clean.go
index ba600d3bb..8345c9af1 100644
--- a/src/cmd/go/clean.go
+++ b/src/cmd/go/clean.go
@@ -106,6 +106,8 @@ func clean(p *Package) {
if cleaned[p] {
return
}
+ cleaned[p] = true
+
if p.Dir == "" {
errorf("can't load package: %v", p.Error)
return
diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go
index a8a9b66aa..3b22e0f04 100644
--- a/src/cmd/go/doc.go
+++ b/src/cmd/go/doc.go
@@ -367,9 +367,10 @@ Compile and run Go program
Usage:
- go run [build flags] gofiles... [arguments...]
+ go run [build flags] [gofiles...] [arguments...]
Run compiles and runs the main package comprising the named Go source files.
+If no files are named, it compiles and runs all non-test Go source files.
For more about build flags, see 'go help build'.
@@ -708,17 +709,18 @@ control the execution of any test:
Print memory allocation statistics for benchmarks.
-benchtime t
- Run enough iterations of each benchmark to take t, specified
- as a time.Duration (for example, -benchtime 1h30s).
- The default is 1 second (1s).
+ Run enough iterations of each benchmark to take t, specified
+ as a time.Duration (for example, -benchtime 1h30s).
+ The default is 1 second (1s).
-blockprofile block.out
Write a goroutine blocking profile to the specified file
when all tests are complete.
-blockprofilerate n
- Control the detail provided in goroutine blocking profiles by setting
- runtime.BlockProfileRate to n. See 'godoc runtime BlockProfileRate'.
+ Control the detail provided in goroutine blocking profiles by
+ calling runtime.SetBlockProfileRate with n.
+ See 'godoc runtime SetBlockProfileRate'.
The profiler aims to sample, on average, one blocking event every
n nanoseconds the program spends blocked. By default,
if -test.blockprofile is set without this flag, all blocking events
@@ -760,10 +762,11 @@ control the execution of any test:
exhaustive tests.
-timeout t
- If a test runs longer than t, panic.
+ If a test runs longer than t, panic.
-v
- Verbose output: log all tests as they are run.
+ Verbose output: log all tests as they are run. Also print all
+ text from Log and Logf calls even if the test succeeds.
The test binary, called pkg.test where pkg is the name of the
directory containing the package sources, can be invoked directly
diff --git a/src/cmd/go/env.go b/src/cmd/go/env.go
index 6cab37b48..00e03e9bd 100644
--- a/src/cmd/go/env.go
+++ b/src/cmd/go/env.go
@@ -42,6 +42,7 @@ func mkEnv() []envVar {
{"GOHOSTOS", runtime.GOOS},
{"GOOS", goos},
{"GOPATH", os.Getenv("GOPATH")},
+ {"GORACE", os.Getenv("GORACE")},
{"GOROOT", goroot},
{"GOTOOLDIR", toolDir},
}
diff --git a/src/cmd/go/mkdoc.sh b/src/cmd/go/mkdoc.sh
index 7768baeb6..12fd7ba3e 100755
--- a/src/cmd/go/mkdoc.sh
+++ b/src/cmd/go/mkdoc.sh
@@ -3,6 +3,7 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
+go install # So the next line will produce updated documentation.
go help documentation > doc.go
gofmt -w doc.go
diff --git a/src/cmd/go/run.go b/src/cmd/go/run.go
index b50569363..d8ba4dbdd 100644
--- a/src/cmd/go/run.go
+++ b/src/cmd/go/run.go
@@ -8,14 +8,16 @@ import (
"fmt"
"os"
"os/exec"
+ "path/filepath"
"strings"
)
var cmdRun = &Command{
- UsageLine: "run [build flags] gofiles... [arguments...]",
+ UsageLine: "run [build flags] [gofiles...] [arguments...]",
Short: "compile and run Go program",
Long: `
Run compiles and runs the main package comprising the named Go source files.
+If no files are named, it compiles and runs all non-test Go source files.
For more about build flags, see 'go help build'.
@@ -44,7 +46,18 @@ func runRun(cmd *Command, args []string) {
}
files, cmdArgs := args[:i], args[i:]
if len(files) == 0 {
- fatalf("go run: no go files listed")
+ allFiles, err := filepath.Glob("*.go")
+ if err != nil {
+ fatalf("go run: %s", err)
+ }
+ for _, file := range allFiles {
+ if !strings.HasSuffix(file, "_test.go") {
+ files = append(files, file)
+ }
+ }
+ if len(files) == 0 {
+ fatalf("go run: no go files found")
+ }
}
for _, file := range files {
if strings.HasSuffix(file, "_test.go") {
diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go
index de69efe8f..58ebcc071 100644
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -98,17 +98,18 @@ control the execution of any test:
Print memory allocation statistics for benchmarks.
-benchtime t
- Run enough iterations of each benchmark to take t, specified
- as a time.Duration (for example, -benchtime 1h30s).
- The default is 1 second (1s).
+ Run enough iterations of each benchmark to take t, specified
+ as a time.Duration (for example, -benchtime 1h30s).
+ The default is 1 second (1s).
-blockprofile block.out
Write a goroutine blocking profile to the specified file
when all tests are complete.
-blockprofilerate n
- Control the detail provided in goroutine blocking profiles by setting
- runtime.BlockProfileRate to n. See 'godoc runtime BlockProfileRate'.
+ Control the detail provided in goroutine blocking profiles by
+ calling runtime.SetBlockProfileRate with n.
+ See 'godoc runtime SetBlockProfileRate'.
The profiler aims to sample, on average, one blocking event every
n nanoseconds the program spends blocked. By default,
if -test.blockprofile is set without this flag, all blocking events
@@ -150,10 +151,11 @@ control the execution of any test:
exhaustive tests.
-timeout t
- If a test runs longer than t, panic.
+ If a test runs longer than t, panic.
-v
- Verbose output: log all tests as they are run.
+ Verbose output: log all tests as they are run. Also print all
+ text from Log and Logf calls even if the test succeeds.
The test binary, called pkg.test where pkg is the name of the
directory containing the package sources, can be invoked directly
diff --git a/src/cmd/godoc/doc.go b/src/cmd/godoc/doc.go
index e5f5324cd..1fa57a8b3 100644
--- a/src/cmd/godoc/doc.go
+++ b/src/cmd/godoc/doc.go
@@ -61,6 +61,8 @@ The flags are:
to the indexer (the indexer will never finish), a value of 1.0
means that index creation is running at full throttle (other
goroutines may get no time while the index is built)
+ -links=true:
+ link identifiers to their declarations
-write_index=false
write index to a file; the file name must be specified with
-index_files
diff --git a/src/cmd/godoc/format.go b/src/cmd/godoc/format.go
index 122ddc7d6..59a89c5bf 100644
--- a/src/cmd/godoc/format.go
+++ b/src/cmd/godoc/format.go
@@ -23,15 +23,21 @@ import (
// ----------------------------------------------------------------------------
// Implementation of FormatSelections
-// A Selection is a function returning offset pairs []int{a, b}
-// describing consecutive non-overlapping text segments [a, b).
-// If there are no more segments, a Selection must return nil.
+// A Segment describes a text segment [start, end).
+// The zero value of a Segment is a ready-to-use empty segment.
//
-// TODO It's more efficient to return a pair (a, b int) instead
-// of creating lots of slices. Need to determine how to
-// indicate the end of a Selection.
+type Segment struct {
+ start, end int
+}
+
+func (seg *Segment) isEmpty() bool { return seg.start >= seg.end }
+
+// A Selection is an "iterator" function returning a text segment.
+// Repeated calls to a selection return consecutive, non-overlapping,
+// non-empty segments, followed by an infinite sequence of empty
+// segments. The first empty segment marks the end of the selection.
//
-type Selection func() []int
+type Selection func() Segment
// A LinkWriter writes some start or end "tag" to w for the text offset offs.
// It is called by FormatSelections at the start or end of each link segment.
@@ -141,17 +147,17 @@ func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection,
//
type merger struct {
selections []Selection
- segments [][]int // segments[i] is the next segment of selections[i]
+ segments []Segment // segments[i] is the next segment of selections[i]
}
const infinity int = 2e9
func newMerger(selections []Selection) *merger {
- segments := make([][]int, len(selections))
+ segments := make([]Segment, len(selections))
for i, sel := range selections {
- segments[i] = []int{infinity, infinity}
+ segments[i] = Segment{infinity, infinity}
if sel != nil {
- if seg := sel(); seg != nil {
+ if seg := sel(); !seg.isEmpty() {
segments[i] = seg
}
}
@@ -170,12 +176,12 @@ func (m *merger) next() (index, offs int, start bool) {
index = -1
for i, seg := range m.segments {
switch {
- case seg[0] < offs:
- offs = seg[0]
+ case seg.start < offs:
+ offs = seg.start
index = i
start = true
- case seg[1] < offs:
- offs = seg[1]
+ case seg.end < offs:
+ offs = seg.end
index = i
start = false
}
@@ -188,18 +194,17 @@ func (m *merger) next() (index, offs int, start bool) {
// either way it is ok to consume the start offset: set it
// to infinity so it won't be considered in the following
// next call
- m.segments[index][0] = infinity
+ m.segments[index].start = infinity
if start {
return
}
// end offset found - consume it
- m.segments[index][1] = infinity
+ m.segments[index].end = infinity
// advance to the next segment for that selection
seg := m.selections[index]()
- if seg == nil {
- return
+ if !seg.isEmpty() {
+ m.segments[index] = seg
}
- m.segments[index] = seg
return
}
@@ -209,7 +214,7 @@ func (m *merger) next() (index, offs int, start bool) {
// lineSelection returns the line segments for text as a Selection.
func lineSelection(text []byte) Selection {
i, j := 0, 0
- return func() (seg []int) {
+ return func() (seg Segment) {
// find next newline, if any
for j < len(text) {
j++
@@ -219,30 +224,30 @@ func lineSelection(text []byte) Selection {
}
if i < j {
// text[i:j] constitutes a line
- seg = []int{i, j}
+ seg = Segment{i, j}
i = j
}
return
}
}
-// commentSelection returns the sequence of consecutive comments
-// in the Go src text as a Selection.
+// tokenSelection returns, as a selection, the sequence of
+// consecutive occurrences of token sel in the Go src text.
//
-func commentSelection(src []byte) Selection {
+func tokenSelection(src []byte, sel token.Token) Selection {
var s scanner.Scanner
fset := token.NewFileSet()
file := fset.AddFile("", fset.Base(), len(src))
s.Init(file, src, nil, scanner.ScanComments)
- return func() (seg []int) {
+ return func() (seg Segment) {
for {
pos, tok, lit := s.Scan()
if tok == token.EOF {
break
}
offs := file.Offset(pos)
- if tok == token.COMMENT {
- seg = []int{offs, offs + len(lit)}
+ if tok == sel {
+ seg = Segment{offs, offs + len(lit)}
break
}
}
@@ -251,13 +256,20 @@ func commentSelection(src []byte) Selection {
}
// makeSelection is a helper function to make a Selection from a slice of pairs.
+// Pairs describing empty segments are ignored.
+//
func makeSelection(matches [][]int) Selection {
- return func() (seg []int) {
- if len(matches) > 0 {
- seg = matches[0]
- matches = matches[1:]
+ i := 0
+ return func() Segment {
+ for i < len(matches) {
+ m := matches[i]
+ i++
+ if m[0] < m[1] {
+ // non-empty segment
+ return Segment{m[0], m[1]}
+ }
}
- return
+ return Segment{}
}
}
@@ -338,7 +350,7 @@ func selectionTag(w io.Writer, text []byte, selections int) {
func FormatText(w io.Writer, text []byte, line int, goSource bool, pattern string, selection Selection) {
var comments, highlights Selection
if goSource {
- comments = commentSelection(text)
+ comments = tokenSelection(text, token.COMMENT)
}
if pattern != "" {
highlights = regexpSelection(text, pattern)
diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go
index 577432113..26b0b97e1 100644
--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -66,6 +66,7 @@ var (
templateDir = flag.String("templates", "", "directory containing alternate template files")
showPlayground = flag.Bool("play", false, "enable playground in web interface")
showExamples = flag.Bool("ex", false, "show examples in command line mode")
+ declLinks = flag.Bool("links", true, "link identifiers to their declarations")
// search index
indexEnabled = flag.Bool("index", false, "enable search index")
@@ -272,17 +273,23 @@ func infoSnippet_htmlFunc(info SpotInfo) string {
return `<span class="alert">no snippet text available</span>`
}
-func nodeFunc(node interface{}, fset *token.FileSet) string {
+func nodeFunc(info *PageInfo, node interface{}) string {
var buf bytes.Buffer
- writeNode(&buf, fset, node)
+ writeNode(&buf, info.FSet, node)
return buf.String()
}
-func node_htmlFunc(node interface{}, fset *token.FileSet) string {
+func node_htmlFunc(info *PageInfo, node interface{}, linkify bool) string {
var buf1 bytes.Buffer
- writeNode(&buf1, fset, node)
+ writeNode(&buf1, info.FSet, node)
+
var buf2 bytes.Buffer
- FormatText(&buf2, buf1.Bytes(), -1, true, "", nil)
+ if n, _ := node.(ast.Node); n != nil && linkify && *declLinks {
+ LinkifyText(&buf2, buf1.Bytes(), n)
+ } else {
+ FormatText(&buf2, buf1.Bytes(), -1, true, "", nil)
+ }
+
return buf2.String()
}
@@ -333,14 +340,14 @@ func stripExampleSuffix(name string) string {
return name
}
-func example_textFunc(funcName string, examples []*doc.Example, fset *token.FileSet, indent string) string {
+func example_textFunc(info *PageInfo, funcName, indent string) string {
if !*showExamples {
return ""
}
var buf bytes.Buffer
first := true
- for _, eg := range examples {
+ for _, eg := range info.Examples {
name := stripExampleSuffix(eg.Name)
if name != funcName {
continue
@@ -354,7 +361,7 @@ func example_textFunc(funcName string, examples []*doc.Example, fset *token.File
// print code
cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments}
var buf1 bytes.Buffer
- writeNode(&buf1, fset, cnode)
+ writeNode(&buf1, info.FSet, cnode)
code := buf1.String()
// Additional formatting if this is a function body.
if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' {
@@ -374,9 +381,9 @@ func example_textFunc(funcName string, examples []*doc.Example, fset *token.File
return buf.String()
}
-func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.FileSet) string {
+func example_htmlFunc(info *PageInfo, funcName string) string {
var buf bytes.Buffer
- for _, eg := range examples {
+ for _, eg := range info.Examples {
name := stripExampleSuffix(eg.Name)
if name != funcName {
@@ -385,7 +392,7 @@ func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.File
// print code
cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments}
- code := node_htmlFunc(cnode, fset)
+ code := node_htmlFunc(info, cnode, true)
out := eg.Output
wholeFile := true
@@ -407,7 +414,7 @@ func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.File
play := ""
if eg.Play != nil && *showPlayground {
var buf bytes.Buffer
- if err := format.Node(&buf, fset, eg.Play); err != nil {
+ if err := format.Node(&buf, info.FSet, eg.Play); err != nil {
log.Print(err)
} else {
play = buf.String()
@@ -472,19 +479,33 @@ func pkgLinkFunc(path string) string {
return pkgHandler.pattern[1:] + relpath // remove trailing '/' for relative URL
}
-func posLink_urlFunc(node ast.Node, fset *token.FileSet) string {
+// n must be an ast.Node or a *doc.Note
+func posLink_urlFunc(info *PageInfo, n interface{}) string {
+ var pos, end token.Pos
+
+ switch n := n.(type) {
+ case ast.Node:
+ pos = n.Pos()
+ end = n.End()
+ case *doc.Note:
+ pos = n.Pos
+ end = n.End
+ default:
+ panic(fmt.Sprintf("wrong type for posLink_url template formatter: %T", n))
+ }
+
var relpath string
var line int
- var low, high int // selection
+ var low, high int // selection offset range
- if p := node.Pos(); p.IsValid() {
- pos := fset.Position(p)
- relpath = pos.Filename
- line = pos.Line
- low = pos.Offset
+ if pos.IsValid() {
+ p := info.FSet.Position(pos)
+ relpath = p.Filename
+ line = p.Line
+ low = p.Offset
}
- if p := node.End(); p.IsValid() {
- high = fset.Position(p).Offset
+ if end.IsValid() {
+ high = info.FSet.Position(end).Offset
}
var buf bytes.Buffer
@@ -521,7 +542,7 @@ var fmap = template.FuncMap{
"filename": filenameFunc,
"repeat": strings.Repeat,
- // accss to FileInfos (directory listings)
+ // access to FileInfos (directory listings)
"fileInfoName": fileInfoNameFunc,
"fileInfoTime": fileInfoTimeFunc,
@@ -1020,6 +1041,23 @@ func collectExamples(pkg *ast.Package, testfiles map[string]*ast.File) []*doc.Ex
return examples
}
+// poorMansImporter returns a (dummy) package object named
+// by the last path component of the provided package path
+// (as is the convention for packages). This is sufficient
+// to resolve package identifiers without doing an actual
+// import. It never returns an error.
+//
+func poorMansImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) {
+ pkg := imports[path]
+ if pkg == nil {
+ // note that strings.LastIndex returns -1 if there is no "/"
+ pkg = ast.NewObj(ast.Pkg, path[strings.LastIndex(path, "/")+1:])
+ pkg.Data = ast.NewScope(nil) // required by ast.NewPackage for dot-import
+ imports[path] = pkg
+ }
+ return pkg, nil
+}
+
// getPageInfo returns the PageInfo for a package directory abspath. If the
// parameter genAST is set, an AST containing only the package exports is
// computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc)
@@ -1028,8 +1066,8 @@ func collectExamples(pkg *ast.Package, testfiles map[string]*ast.File) []*doc.Ex
// directories, PageInfo.Dirs is nil. If an error occurred, PageInfo.Err is
// set to the respective error but the error is not logged.
//
-func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) (info PageInfo) {
- info.Dirname = abspath
+func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo {
+ info := &PageInfo{Dirname: abspath}
// Restrict to the package files that would be used when building
// the package on this system. This makes sure that if there are
@@ -1046,7 +1084,7 @@ func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) (inf
// continue if there are no Go source files; we still want the directory info
if _, nogo := err.(*build.NoGoError); err != nil && !nogo {
info.Err = err
- return
+ return info
}
// collect package files
@@ -1069,9 +1107,11 @@ func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) (inf
files, err := parseFiles(fset, abspath, pkgfiles)
if err != nil {
info.Err = err
- return
+ return info
}
- pkg := &ast.Package{Name: pkgname, Files: files}
+
+ // ignore any errors - they are due to unresolved identifiers
+ pkg, _ := ast.NewPackage(fset, files, poorMansImporter, nil)
// extract package documentation
info.FSet = fset
@@ -1143,7 +1183,7 @@ func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) (inf
info.DirTime = timestamp
info.DirFlat = mode&flatDir != 0
- return
+ return info
}
func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -1475,6 +1515,8 @@ func readIndex(filenames string) error {
matches, err := filepath.Glob(filenames)
if err != nil {
return err
+ } else if matches == nil {
+ return fmt.Errorf("no index files match %q", filenames)
}
sort.Strings(matches) // make sure files are in the right order
files := make([]io.Reader, 0, len(matches))
diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go
index 91c56461a..8198fca0d 100644
--- a/src/cmd/godoc/index.go
+++ b/src/cmd/godoc/index.go
@@ -651,8 +651,6 @@ func (x *Indexer) addFile(filename string, goFile bool) (file *token.File, ast *
// makes sure that the important files are included and massively reduces the
// number of files to index. The advantage over a blacklist is that unexpected
// (non-blacklisted) files won't suddenly explode the index.
-//
-// TODO(gri): We may want to make this list customizable, perhaps via a flag.
// Files are whitelisted if they have a file name or extension
// present as key in whitelisted.
diff --git a/src/cmd/godoc/linkify.go b/src/cmd/godoc/linkify.go
new file mode 100644
index 000000000..5b4862419
--- /dev/null
+++ b/src/cmd/godoc/linkify.go
@@ -0,0 +1,234 @@
+// 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 implements LinkifyText which introduces
+// links for identifiers pointing to their declarations.
+// The approach does not cover all cases because godoc
+// doesn't have complete type information, but it's
+// reasonably good for browsing.
+
+package main
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "io"
+ "strconv"
+)
+
+// LinkifyText HTML-escapes source text and writes it to w.
+// Identifiers that are in a "use" position (i.e., that are
+// not being declared), are wrapped with HTML links pointing
+// to the respective declaration, if possible. Comments are
+// formatted the same way as with FormatText.
+//
+func LinkifyText(w io.Writer, text []byte, n ast.Node) {
+ links := linksFor(n)
+
+ i := 0 // links index
+ prev := "" // prev HTML tag
+ linkWriter := func(w io.Writer, _ int, start bool) {
+ // end tag
+ if !start {
+ if prev != "" {
+ fmt.Fprintf(w, `</%s>`, prev)
+ prev = ""
+ }
+ return
+ }
+
+ // start tag
+ prev = ""
+ if i < len(links) {
+ switch info := links[i]; {
+ case info.path != "" && info.name == "":
+ // package path
+ fmt.Fprintf(w, `<a href="/pkg/%s/">`, info.path)
+ prev = "a"
+ case info.path != "" && info.name != "":
+ // qualified identifier
+ fmt.Fprintf(w, `<a href="/pkg/%s/#%s">`, info.path, info.name)
+ prev = "a"
+ case info.path == "" && info.name != "":
+ // local identifier
+ if info.mode == identVal {
+ fmt.Fprintf(w, `<span id="%s">`, info.name)
+ prev = "span"
+ } else {
+ fmt.Fprintf(w, `<a href="#%s">`, info.name)
+ prev = "a"
+ }
+ }
+ i++
+ }
+ }
+
+ idents := tokenSelection(text, token.IDENT)
+ comments := tokenSelection(text, token.COMMENT)
+ FormatSelections(w, text, linkWriter, idents, selectionTag, comments)
+}
+
+// A link describes the (HTML) link information for an identifier.
+// The zero value of a link represents "no link".
+//
+type link struct {
+ mode identMode
+ path, name string // package path, identifier name
+}
+
+// linksFor returns the list of links for the identifiers used
+// by node in the same order as they appear in the source.
+//
+func linksFor(node ast.Node) (list []link) {
+ modes := identModesFor(node)
+
+ // NOTE: We are expecting ast.Inspect to call the
+ // callback function in source text order.
+ ast.Inspect(node, func(node ast.Node) bool {
+ switch n := node.(type) {
+ case *ast.Ident:
+ m := modes[n]
+ info := link{mode: m}
+ switch m {
+ case identUse:
+ if n.Obj == nil && predeclared[n.Name] {
+ info.path = builtinPkgPath
+ }
+ info.name = n.Name
+ case identDef:
+ // any declaration expect const or var - empty link
+ case identVal:
+ // const or var declaration
+ info.name = n.Name
+ }
+ list = append(list, info)
+ return false
+ case *ast.SelectorExpr:
+ // Detect qualified identifiers of the form pkg.ident.
+ // If anything fails we return true and collect individual
+ // identifiers instead.
+ if x, _ := n.X.(*ast.Ident); x != nil {
+ // x must be a package for a qualified identifier
+ if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg {
+ if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil {
+ // spec.Path.Value is the import path
+ if path, err := strconv.Unquote(spec.Path.Value); err == nil {
+ // Register two links, one for the package
+ // and one for the qualified identifier.
+ info := link{path: path}
+ list = append(list, info)
+ info.name = n.Sel.Name
+ list = append(list, info)
+ return false
+ }
+ }
+ }
+ }
+ }
+ return true
+ })
+
+ return
+}
+
+// The identMode describes how an identifier is "used" at its source location.
+type identMode int
+
+const (
+ identUse identMode = iota // identifier is used (must be zero value for identMode)
+ identDef // identifier is defined
+ identVal // identifier is defined in a const or var declaration
+)
+
+// identModesFor returns a map providing the identMode for each identifier used by node.
+func identModesFor(node ast.Node) map[*ast.Ident]identMode {
+ m := make(map[*ast.Ident]identMode)
+
+ ast.Inspect(node, func(node ast.Node) bool {
+ switch n := node.(type) {
+ case *ast.Field:
+ for _, n := range n.Names {
+ m[n] = identDef
+ }
+ case *ast.ImportSpec:
+ if name := n.Name; name != nil {
+ m[name] = identDef
+ }
+ case *ast.ValueSpec:
+ for _, n := range n.Names {
+ m[n] = identVal
+ }
+ case *ast.TypeSpec:
+ m[n.Name] = identDef
+ case *ast.FuncDecl:
+ m[n.Name] = identDef
+ case *ast.AssignStmt:
+ // Short variable declarations only show up if we apply
+ // this code to all source code (as opposed to exported
+ // declarations only).
+ if n.Tok == token.DEFINE {
+ // Some of the lhs variables may be re-declared,
+ // so technically they are not defs. We don't
+ // care for now.
+ for _, x := range n.Lhs {
+ // Each lhs expression should be an
+ // ident, but we are conservative and check.
+ if n, _ := x.(*ast.Ident); n != nil {
+ m[n] = identVal
+ }
+ }
+ }
+ }
+ return true
+ })
+
+ return m
+}
+
+// The predeclared map represents the set of all predeclared identifiers.
+// TODO(gri) This information is also encoded in similar maps in go/doc,
+// but not exported. Consider exporting an accessor and using
+// it instead.
+var predeclared = map[string]bool{
+ "bool": true,
+ "byte": true,
+ "complex64": true,
+ "complex128": true,
+ "error": true,
+ "float32": true,
+ "float64": true,
+ "int": true,
+ "int8": true,
+ "int16": true,
+ "int32": true,
+ "int64": true,
+ "rune": true,
+ "string": true,
+ "uint": true,
+ "uint8": true,
+ "uint16": true,
+ "uint32": true,
+ "uint64": true,
+ "uintptr": true,
+ "true": true,
+ "false": true,
+ "iota": true,
+ "nil": true,
+ "append": true,
+ "cap": true,
+ "close": true,
+ "complex": true,
+ "copy": true,
+ "delete": true,
+ "imag": true,
+ "len": true,
+ "make": true,
+ "new": true,
+ "panic": true,
+ "print": true,
+ "println": true,
+ "real": true,
+ "recover": true,
+}
diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go
index 134410090..81e739d20 100644
--- a/src/cmd/godoc/main.go
+++ b/src/cmd/godoc/main.go
@@ -374,26 +374,26 @@ func main() {
}
// first, try as package unless forced as command
- var info PageInfo
+ var info *PageInfo
if !forceCmd {
info = pkgHandler.getPageInfo(abspath, relpath, mode)
}
// second, try as command unless the path is absolute
// (the go command invokes godoc w/ absolute paths; don't override)
- var cinfo PageInfo
+ var cinfo *PageInfo
if !filepath.IsAbs(path) {
abspath = pathpkg.Join(cmdHandler.fsRoot, path)
cinfo = cmdHandler.getPageInfo(abspath, relpath, mode)
}
// determine what to use
- if info.IsEmpty() {
- if !cinfo.IsEmpty() {
+ if info == nil || info.IsEmpty() {
+ if cinfo != nil && !cinfo.IsEmpty() {
// only cinfo exists - switch to cinfo
info = cinfo
}
- } else if !cinfo.IsEmpty() {
+ } else if cinfo != nil && !cinfo.IsEmpty() {
// both info and cinfo exist - use cinfo if info
// contains only subdirectory information
if info.PAst == nil && info.PDoc == nil {
@@ -403,15 +403,19 @@ func main() {
}
}
+ if info == nil {
+ log.Fatalf("%s: no such directory or package", flag.Arg(0))
+ }
if info.Err != nil {
log.Fatalf("%v", info.Err)
}
+
if info.PDoc != nil && info.PDoc.ImportPath == target {
// Replace virtual /target with actual argument from command line.
info.PDoc.ImportPath = flag.Arg(0)
}
- // If we have more than one argument, use the remaining arguments for filtering
+ // If we have more than one argument, use the remaining arguments for filtering.
if flag.NArg() > 1 {
args := flag.Args()[1:]
rx := makeRx(args)
diff --git a/src/cmd/godoc/utils.go b/src/cmd/godoc/utils.go
index 7def015c8..0cdb7ff7a 100644
--- a/src/cmd/godoc/utils.go
+++ b/src/cmd/godoc/utils.go
@@ -56,8 +56,6 @@ func isText(s []byte) bool {
return true
}
-// TODO(gri): Should have a mapping from extension to handler, eventually.
-
// textExt[x] is true if the extension x indicates a text file, and false otherwise.
var textExt = map[string]bool{
".css": false, // must be served raw
diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go
index 202d0a50c..75a322a6c 100644
--- a/src/cmd/gofmt/gofmt_test.go
+++ b/src/cmd/gofmt/gofmt_test.go
@@ -82,6 +82,9 @@ var tests = []struct {
{"testdata/rewrite3.input", "-r=x->x"},
{"testdata/rewrite4.input", "-r=(x)->x"},
{"testdata/rewrite5.input", "-r=x+x->2*x"},
+ {"testdata/rewrite6.input", "-r=fun(x)->Fun(x)"},
+ {"testdata/rewrite7.input", "-r=fun(x...)->Fun(x)"},
+ {"testdata/rewrite8.input", "-r=interface{}->int"},
{"testdata/stdin*.input", "-stdin"},
{"testdata/comments.input", ""},
{"testdata/import.input", ""},
diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go
index dfabb6198..66d2331a5 100644
--- a/src/cmd/gofmt/rewrite.go
+++ b/src/cmd/gofmt/rewrite.go
@@ -35,10 +35,10 @@ func initRewrite() {
// It might make sense to expand this to allow statement patterns,
// but there are problems with preserving formatting and also
// with what a wildcard for a statement looks like.
-func parseExpr(s string, what string) ast.Expr {
+func parseExpr(s, what string) ast.Expr {
x, err := parser.ParseExpr(s)
if err != nil {
- fmt.Fprintf(os.Stderr, "parsing %s %s: %s\n", what, s, err)
+ fmt.Fprintf(os.Stderr, "parsing %s %s at %s\n", what, s, err)
os.Exit(2)
}
return x
@@ -107,6 +107,7 @@ var (
identType = reflect.TypeOf((*ast.Ident)(nil))
objectPtrType = reflect.TypeOf((*ast.Object)(nil))
positionType = reflect.TypeOf(token.NoPos)
+ callExprType = reflect.TypeOf((*ast.CallExpr)(nil))
scopePtrType = reflect.TypeOf((*ast.Scope)(nil))
)
@@ -192,8 +193,17 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
v := val.Interface().(*ast.Ident)
return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name
case objectPtrType, positionType:
- // object pointers and token positions don't need to match
+ // object pointers and token positions always match
return true
+ case callExprType:
+ // For calls, the Ellipsis fields (token.Position) must
+ // match since that is how f(x) and f(x...) are different.
+ // Check them here but fall through for the remaining fields.
+ p := pattern.Interface().(*ast.CallExpr)
+ v := val.Interface().(*ast.CallExpr)
+ if p.Ellipsis.IsValid() != v.Ellipsis.IsValid() {
+ return false
+ }
}
p := reflect.Indirect(pattern)
diff --git a/src/cmd/gofmt/testdata/rewrite6.golden b/src/cmd/gofmt/testdata/rewrite6.golden
new file mode 100644
index 000000000..e565dbdd9
--- /dev/null
+++ b/src/cmd/gofmt/testdata/rewrite6.golden
@@ -0,0 +1,15 @@
+// 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.
+
+// Rewriting of calls must take the ... (ellipsis)
+// attribute for the last argument into account.
+
+package p
+
+func fun(x []int) {}
+
+func g(x []int) {
+ Fun(x) // -r='fun(x)->Fun(x)' should rewrite this to Fun(x)
+ fun(x...) // -r='fun(x)->Fun(x)' should not rewrite this
+}
diff --git a/src/cmd/gofmt/testdata/rewrite6.input b/src/cmd/gofmt/testdata/rewrite6.input
new file mode 100644
index 000000000..8c088b3e8
--- /dev/null
+++ b/src/cmd/gofmt/testdata/rewrite6.input
@@ -0,0 +1,15 @@
+// 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.
+
+// Rewriting of calls must take the ... (ellipsis)
+// attribute for the last argument into account.
+
+package p
+
+func fun(x []int) {}
+
+func g(x []int) {
+ fun(x) // -r='fun(x)->Fun(x)' should rewrite this to Fun(x)
+ fun(x...) // -r='fun(x)->Fun(x)' should not rewrite this
+}
diff --git a/src/cmd/gofmt/testdata/rewrite7.golden b/src/cmd/gofmt/testdata/rewrite7.golden
new file mode 100644
index 000000000..29babad9f
--- /dev/null
+++ b/src/cmd/gofmt/testdata/rewrite7.golden
@@ -0,0 +1,15 @@
+// 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.
+
+// Rewriting of calls must take the ... (ellipsis)
+// attribute for the last argument into account.
+
+package p
+
+func fun(x []int) {}
+
+func g(x []int) {
+ fun(x) // -r='fun(x...)->Fun(x)' should not rewrite this
+ Fun(x) // -r='fun(x...)->Fun(x)' should rewrite this to Fun(x)
+}
diff --git a/src/cmd/gofmt/testdata/rewrite7.input b/src/cmd/gofmt/testdata/rewrite7.input
new file mode 100644
index 000000000..073e2a3e6
--- /dev/null
+++ b/src/cmd/gofmt/testdata/rewrite7.input
@@ -0,0 +1,15 @@
+// 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.
+
+// Rewriting of calls must take the ... (ellipsis)
+// attribute for the last argument into account.
+
+package p
+
+func fun(x []int) {}
+
+func g(x []int) {
+ fun(x) // -r='fun(x...)->Fun(x)' should not rewrite this
+ fun(x...) // -r='fun(x...)->Fun(x)' should rewrite this to Fun(x)
+}
diff --git a/src/cmd/gofmt/testdata/rewrite8.golden b/src/cmd/gofmt/testdata/rewrite8.golden
new file mode 100644
index 000000000..cfc452b03
--- /dev/null
+++ b/src/cmd/gofmt/testdata/rewrite8.golden
@@ -0,0 +1,10 @@
+// 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.
+
+// Check that literal type expression rewrites are accepted.
+// Was issue 4406.
+
+package p
+
+type T int
diff --git a/src/cmd/gofmt/testdata/rewrite8.input b/src/cmd/gofmt/testdata/rewrite8.input
new file mode 100644
index 000000000..235efa91c
--- /dev/null
+++ b/src/cmd/gofmt/testdata/rewrite8.input
@@ -0,0 +1,10 @@
+// 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.
+
+// Check that literal type expression rewrites are accepted.
+// Was issue 4406.
+
+package p
+
+type T interface{}
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c
index b70d4636c..fa34aa3a1 100644
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -172,7 +172,7 @@ relocsym(Sym *s)
if(r->sym != S && r->sym->type == SDYNIMPORT)
diag("unhandled relocation for %s (type %d rtype %d)", r->sym->name, r->sym->type, r->type);
- if(r->sym != S && !r->sym->reachable)
+ if(r->sym != S && r->sym->type != STLSBSS && !r->sym->reachable)
diag("unreachable sym in relocation: %s %s", s->name, r->sym->name);
switch(r->type) {
@@ -181,6 +181,10 @@ relocsym(Sym *s)
if(linkmode == LinkExternal || archreloc(r, s, &o) < 0)
diag("unknown reloc %d", r->type);
break;
+ case D_TLS:
+ r->done = 0;
+ o = 0;
+ break;
case D_ADDR:
if(linkmode == LinkExternal && r->sym->type != SCONST) {
r->done = 0;
@@ -646,7 +650,7 @@ datblk(int32 addr, int32 size)
break;
}
Bprint(&bso, "\treloc %.8ux/%d %s %s+%#llx [%#llx]\n",
- (uint)(sym->value+r->off), r->siz, typ, rsname, r->add, r->sym->value+r->add);
+ (uint)(sym->value+r->off), r->siz, typ, rsname, (vlong)r->add, (vlong)(r->sym->value+r->add));
}
}
}
@@ -1193,11 +1197,7 @@ dodata(void)
sect->vaddr = datsize;
lookup("noptrbss", 0)->sect = sect;
lookup("enoptrbss", 0)->sect = sect;
- for(; s != nil; s = s->next) {
- if(s->type > SNOPTRBSS) {
- cursym = s;
- diag("unexpected symbol type %d", s->type);
- }
+ for(; s != nil && s->type == SNOPTRBSS; s = s->next) {
datsize = aligndatsize(datsize, s);
s->sect = sect;
s->value = datsize;
@@ -1205,10 +1205,28 @@ dodata(void)
}
sect->len = datsize - sect->vaddr;
lookup("end", 0)->sect = sect;
+
+ if(iself && linkmode == LinkExternal && s != nil && s->type == STLSBSS && HEADTYPE != Hopenbsd) {
+ sect = addsection(&segdata, ".tbss", 06);
+ sect->align = PtrSize;
+ sect->vaddr = 0;
+ datsize = 0;
+ for(; s != nil && s->type == STLSBSS; s = s->next) {
+ datsize = aligndatsize(datsize, s);
+ s->sect = sect;
+ s->value = datsize;
+ datsize += s->size;
+ }
+ sect->len = datsize;
+ }
+
+ if(s != nil) {
+ cursym = nil;
+ diag("unexpected symbol type %d for %s", s->type, s->name);
+ }
/* we finished segdata, begin segtext */
s = datap;
- datsize = 0;
/* read-only data */
sect = addsection(&segtext, ".rodata", 04);
diff --git a/src/cmd/ld/doc.go b/src/cmd/ld/doc.go
index bad4e540f..874db41c9 100644
--- a/src/cmd/ld/doc.go
+++ b/src/cmd/ld/doc.go
@@ -71,5 +71,22 @@ Options new in this version:
NOTE: it only eliminates false positives caused by other function
calls, not false positives caused by dead temporaries stored in
the current function call.
+ -linkmode argument
+ Set the linkmode. The argument must be one of
+ internal, external, or auto. The default is auto.
+ This sets the linking mode as described in
+ ../cgo/doc.go.
+ -tmpdir dir
+ Set the location to use for any temporary files. The
+ default is a newly created directory that is removed
+ after the linker completes. Temporary files are only
+ used in external linking mode.
+ -extld name
+ Set the name of the external linker to use in external
+ linking mode. The default is "gcc".
+ -extldflags flags
+ Set space-separated trailing flags to pass to the
+ external linker in external linking mode. The default
+ is to not pass any additional trailing flags.
*/
package main
diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c
index 4bf788e64..436e1e67e 100644
--- a/src/cmd/ld/dwarf.c
+++ b/src/cmd/ld/dwarf.c
@@ -1130,14 +1130,24 @@ mkinternaltypename(char *base, char *arg1, char *arg2)
return n;
}
+// synthesizemaptypes is way too closely married to runtime/hashmap.c
+enum {
+ MaxKeySize = 128,
+ MaxValSize = 128,
+ BucketSize = 8,
+};
static void
synthesizemaptypes(DWDie *die)
{
- DWDie *hash, *dwh, *keytype, *valtype;
+ DWDie *hash, *bucket, *dwh, *dwhk, *dwhv, *dwhb, *keytype, *valtype, *fld;
+ int indirect_key, indirect_val;
+ int keysize, valsize;
+ DWAttr *a;
- hash = defgotype(lookup_or_diag("type.runtime.hmap"));
+ hash = walktypedef(defgotype(lookup_or_diag("type.runtime.hmap")));
+ bucket = walktypedef(defgotype(lookup_or_diag("type.runtime.bucket")));
if (hash == nil)
return;
@@ -1146,8 +1156,59 @@ synthesizemaptypes(DWDie *die)
if (die->abbrev != DW_ABRV_MAPTYPE)
continue;
- keytype = (DWDie*) getattr(die, DW_AT_internal_key_type)->data;
- valtype = (DWDie*) getattr(die, DW_AT_internal_val_type)->data;
+ keytype = walktypedef((DWDie*) getattr(die, DW_AT_internal_key_type)->data);
+ valtype = walktypedef((DWDie*) getattr(die, DW_AT_internal_val_type)->data);
+
+ // compute size info like hashmap.c does.
+ a = getattr(keytype, DW_AT_byte_size);
+ keysize = a ? a->value : PtrSize; // We don't store size with Pointers
+ a = getattr(valtype, DW_AT_byte_size);
+ valsize = a ? a->value : PtrSize;
+ indirect_key = 0;
+ indirect_val = 0;
+ if(keysize > MaxKeySize) {
+ keysize = PtrSize;
+ indirect_key = 1;
+ }
+ if(valsize > MaxValSize) {
+ valsize = PtrSize;
+ indirect_val = 1;
+ }
+
+ // Construct type to represent an array of BucketSize keys
+ dwhk = newdie(&dwtypes, DW_ABRV_ARRAYTYPE,
+ mkinternaltypename("[]key",
+ getattr(keytype, DW_AT_name)->data, nil));
+ newattr(dwhk, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize * keysize, 0);
+ newrefattr(dwhk, DW_AT_type, indirect_key ? defptrto(keytype) : keytype);
+ fld = newdie(dwhk, DW_ABRV_ARRAYRANGE, "size");
+ newattr(fld, DW_AT_upper_bound, DW_CLS_CONSTANT, BucketSize, 0);
+ newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr"));
+
+ // Construct type to represent an array of BucketSize values
+ dwhv = newdie(&dwtypes, DW_ABRV_ARRAYTYPE,
+ mkinternaltypename("[]val",
+ getattr(valtype, DW_AT_name)->data, nil));
+ newattr(dwhv, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize * valsize, 0);
+ newrefattr(dwhv, DW_AT_type, indirect_val ? defptrto(valtype) : valtype);
+ fld = newdie(dwhv, DW_ABRV_ARRAYRANGE, "size");
+ newattr(fld, DW_AT_upper_bound, DW_CLS_CONSTANT, BucketSize, 0);
+ newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr"));
+
+ // Construct bucket<K,V>
+ dwhb = newdie(&dwtypes, DW_ABRV_STRUCTTYPE,
+ mkinternaltypename("bucket",
+ getattr(keytype, DW_AT_name)->data,
+ getattr(valtype, DW_AT_name)->data));
+ copychildren(dwhb, bucket);
+ fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "keys");
+ newrefattr(fld, DW_AT_type, dwhk);
+ newmemberoffsetattr(fld, BucketSize + PtrSize);
+ fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "values");
+ newrefattr(fld, DW_AT_type, dwhv);
+ newmemberoffsetattr(fld, BucketSize + PtrSize + BucketSize * keysize);
+ newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize + PtrSize + BucketSize * keysize + BucketSize * valsize, 0);
+ substitutetype(dwhb, "overflow", defptrto(dwhb));
// Construct hash<K,V>
dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE,
@@ -1155,9 +1216,12 @@ synthesizemaptypes(DWDie *die)
getattr(keytype, DW_AT_name)->data,
getattr(valtype, DW_AT_name)->data));
copychildren(dwh, hash);
+ substitutetype(dwh, "buckets", defptrto(dwhb));
+ substitutetype(dwh, "oldbuckets", defptrto(dwhb));
newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT,
getattr(hash, DW_AT_byte_size)->value, nil);
+ // make map type a pointer to hash<K,V>
newrefattr(die, DW_AT_type, defptrto(dwh));
}
}
diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c
index f5cce9c52..22bc64f8f 100644
--- a/src/cmd/ld/elf.c
+++ b/src/cmd/ld/elf.c
@@ -758,6 +758,10 @@ elfshbits(Section *sect)
sh->flags |= SHF_EXECINSTR;
if(sect->rwx & 2)
sh->flags |= SHF_WRITE;
+ if(strcmp(sect->name, ".tbss") == 0) {
+ sh->flags |= SHF_TLS;
+ sh->type = SHT_NOBITS;
+ }
if(linkmode != LinkExternal)
sh->addr = sect->vaddr;
sh->addralign = sect->align;
@@ -779,7 +783,7 @@ elfshreloc(Section *sect)
// Also nothing to relocate in .shstrtab.
if(sect->vaddr >= sect->seg->vaddr + sect->seg->filelen)
return nil;
- if(strcmp(sect->name, ".shstrtab") == 0)
+ if(strcmp(sect->name, ".shstrtab") == 0 || strcmp(sect->name, ".tbss") == 0)
return nil;
if(thechar == '6') {
@@ -883,6 +887,8 @@ doelf(void)
addstring(shstrtab, ".data");
addstring(shstrtab, ".bss");
addstring(shstrtab, ".noptrbss");
+ if(linkmode == LinkExternal && HEADTYPE != Hopenbsd)
+ addstring(shstrtab, ".tbss");
if(HEADTYPE == Hnetbsd)
addstring(shstrtab, ".note.netbsd.ident");
if(HEADTYPE == Hopenbsd)
diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c
index f933cbba3..fa2ec4e28 100644
--- a/src/cmd/ld/go.c
+++ b/src/cmd/ld/go.c
@@ -508,10 +508,6 @@ loadcgo(char *file, char *pkg, char *p, int n)
}
if(s->cgoexport == 0) {
- if(strcmp(f[0], "cgo_export_static") == 0)
- s->cgoexport |= CgoExportStatic;
- else
- s->cgoexport |= CgoExportDynamic;
s->extname = remote;
if(ndynexp%32 == 0)
dynexp = erealloc(dynexp, (ndynexp+32)*sizeof dynexp[0]);
@@ -521,6 +517,10 @@ loadcgo(char *file, char *pkg, char *p, int n)
nerrors++;
return;
}
+ if(strcmp(f[0], "cgo_export_static") == 0)
+ s->cgoexport |= CgoExportStatic;
+ else
+ s->cgoexport |= CgoExportDynamic;
if(local != f[1])
free(local);
continue;
diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c
index 18cae3175..84777b1a9 100644
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -31,6 +31,7 @@
#include "l.h"
#include "lib.h"
+#include "../ld/elf.h"
#include "../../pkg/runtime/stack.h"
#include <ar.h>
@@ -608,7 +609,7 @@ void
hostlink(void)
{
char *p, **argv;
- int i, w, n, argc, len;
+ int c, i, w, n, argc, len;
Hostobj *h;
Biobuf *f;
static char buf[64<<10];
@@ -616,11 +617,22 @@ hostlink(void)
if(linkmode != LinkExternal || nerrors > 0)
return;
- argv = malloc((10+nhostobj+nldflag)*sizeof argv[0]);
+ c = 0;
+ p = extldflags;
+ while(p != nil) {
+ while(*p == ' ')
+ p++;
+ if(*p == '\0')
+ break;
+ c++;
+ p = strchr(p + 1, ' ');
+ }
+
+ argv = malloc((10+nhostobj+nldflag+c)*sizeof argv[0]);
argc = 0;
- // TODO: Add command-line flag to override gcc path and specify additional leading options.
- // TODO: Add command-line flag to specify additional trailing options.
- argv[argc++] = "gcc";
+ if(extld == nil)
+ extld = "gcc";
+ argv[argc++] = extld;
switch(thechar){
case '8':
argv[argc++] = "-m32";
@@ -636,9 +648,12 @@ hostlink(void)
argv[argc++] = "-o";
argv[argc++] = outfile;
+ if(rpath)
+ argv[argc++] = smprint("-Wl,-rpath,%s", rpath);
+
// Force global symbols to be exported for dlopen, etc.
- // NOTE: May not work on OS X or Windows. We'll see.
- argv[argc++] = "-rdynamic";
+ if(iself)
+ argv[argc++] = "-rdynamic";
// already wrote main object file
// copy host objects to temporary directory
@@ -675,6 +690,17 @@ hostlink(void)
argv[argc++] = smprint("%s/go.o", tmpdir);
for(i=0; i<nldflag; i++)
argv[argc++] = ldflag[i];
+
+ p = extldflags;
+ while(p != nil) {
+ while(*p == ' ')
+ *p++ = '\0';
+ if(*p == '\0')
+ break;
+ argv[argc++] = p;
+ p = strchr(p + 1, ' ');
+ }
+
argv[argc] = nil;
quotefmtinstall();
@@ -846,17 +872,16 @@ _lookup(char *symb, int v, int creat)
Sym *s;
char *p;
int32 h;
- int l, c;
+ int c;
h = v;
for(p=symb; c = *p; p++)
h = h+h+h + c;
- l = (p - symb) + 1;
// not if(h < 0) h = ~h, because gcc 4.3 -O2 miscompiles it.
h &= 0xffffff;
h %= NHASH;
for(s = hash[h]; s != S; s = s->hash)
- if(memcmp(s->name, symb, l) == 0)
+ if(strcmp(s->name, symb) == 0)
return s;
if(!creat)
return nil;
@@ -1860,7 +1885,10 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*))
/* frame, locals, args, auto and param after */
put(nil, ".frame", 'm', (uint32)s->text->to.offset+PtrSize, 0, 0, 0);
put(nil, ".locals", 'm', s->locals, 0, 0, 0);
- put(nil, ".args", 'm', s->args, 0, 0, 0);
+ if(s->text->textflag & NOSPLIT)
+ put(nil, ".args", 'm', ArgsSizeUnknown, 0, 0, 0);
+ else
+ put(nil, ".args", 'm', s->args, 0, 0, 0);
for(a=s->autom; a; a=a->link) {
// Emit a or p according to actual offset, even if label is wrong.
diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h
index a5ca7d3c3..9bdfe95c4 100644
--- a/src/cmd/ld/lib.h
+++ b/src/cmd/ld/lib.h
@@ -52,6 +52,7 @@ enum
SWINDOWS,
SBSS,
SNOPTRBSS,
+ STLSBSS,
SXREF,
SMACHOSYMSTR,
@@ -70,6 +71,13 @@ enum
NHASH = 100003,
};
+enum
+{
+ // This value is known to the garbage collector and should be kept in
+ // sync with runtime/pkg/runtime.h
+ ArgsSizeUnknown = 0x80000000
+};
+
typedef struct Library Library;
struct Library
{
@@ -149,6 +157,8 @@ EXTERN int flag_shared;
EXTERN char* tracksym;
EXTERN char* interpreter;
EXTERN char* tmpdir;
+EXTERN char* extld;
+EXTERN char* extldflags;
enum
{
diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c
index 698194f84..d8a4645e0 100644
--- a/src/cmd/ld/symtab.c
+++ b/src/cmd/ld/symtab.c
@@ -152,6 +152,26 @@ asmelfsym(void)
elfbind = STB_LOCAL;
genasmsym(putelfsym);
+
+ if(linkmode == LinkExternal && HEADTYPE != Hopenbsd) {
+ s = lookup("runtime.m", 0);
+ if(s->sect == nil) {
+ cursym = nil;
+ diag("missing section for %s", s->name);
+ errorexit();
+ }
+ putelfsyment(putelfstr(s->name), 0, PtrSize, (STB_LOCAL<<4)|STT_TLS, s->sect->elfsect->shnum, 0);
+ s->elfsym = numelfsym++;
+
+ s = lookup("runtime.g", 0);
+ if(s->sect == nil) {
+ cursym = nil;
+ diag("missing section for %s", s->name);
+ errorexit();
+ }
+ putelfsyment(putelfstr(s->name), PtrSize, PtrSize, (STB_LOCAL<<4)|STT_TLS, s->sect->elfsect->shnum, 0);
+ s->elfsym = numelfsym++;
+ }
elfbind = STB_GLOBAL;
elfglobalsymndx = numelfsym;
diff --git a/src/cmd/nm/nm.c b/src/cmd/nm/nm.c
index 668239035..aa056b882 100644
--- a/src/cmd/nm/nm.c
+++ b/src/cmd/nm/nm.c
@@ -275,11 +275,13 @@ psym(Sym *s, void* p)
return;
break;
case 'm':
+ if(!aflag || uflag || gflag)
+ return;
+ break;
case 'f': /* we only see a 'z' when the following is true*/
if(!aflag || uflag || gflag)
return;
- if (strcmp(s->name, ".frame"))
- zenter(s);
+ zenter(s);
break;
case 'a':
case 'p':
diff --git a/src/cmd/vet/asmdecl.go b/src/cmd/vet/asmdecl.go
index 2cfaa938f..c23514c28 100644
--- a/src/cmd/vet/asmdecl.go
+++ b/src/cmd/vet/asmdecl.go
@@ -104,6 +104,7 @@ func asmCheck(pkg *Package) {
if !strings.HasSuffix(f.name, ".s") {
continue
}
+ Println("Checking file", f.name)
// Determine architecture from file name if possible.
var arch string
diff --git a/src/cmd/vet/test_asm1.s b/src/cmd/vet/test_asm1.s
index 2fe31963a..8cd9eeab6 100644
--- a/src/cmd/vet/test_asm1.s
+++ b/src/cmd/vet/test_asm1.s
@@ -3,9 +3,7 @@
// license that can be found in the LICENSE file.
// +build amd64
-// +build ignore
-
-package main
+// +build vet_test
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), AX
diff --git a/src/cmd/vet/test_asm2.s b/src/cmd/vet/test_asm2.s
index 73a4ad4be..d8679c574 100644
--- a/src/cmd/vet/test_asm2.s
+++ b/src/cmd/vet/test_asm2.s
@@ -3,9 +3,7 @@
// license that can be found in the LICENSE file.
// +build 386
-// +build ignore
-
-package main
+// +build vet_test
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), AX
diff --git a/src/cmd/vet/test_asm3.s b/src/cmd/vet/test_asm3.s
index 2c1a54f3e..bf98805a2 100644
--- a/src/cmd/vet/test_asm3.s
+++ b/src/cmd/vet/test_asm3.s
@@ -3,9 +3,7 @@
// license that can be found in the LICENSE file.
// +build arm
-// +build ignore
-
-package main
+// +build vet_test
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), AX
diff --git a/src/cmd/vet/test_deadcode.go b/src/cmd/vet/test_deadcode.go
index 3ded80ca2..d08e57782 100644
--- a/src/cmd/vet/test_deadcode.go
+++ b/src/cmd/vet/test_deadcode.go
@@ -112,31 +112,31 @@ func _() int {
func _() int {
print(1)
return 2
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
func _() int {
L:
print(1)
goto L
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
func _() int {
print(1)
panic(2)
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
func _() int {
{
print(1)
return 2
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
}
@@ -145,8 +145,8 @@ L:
{
print(1)
goto L
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
}
@@ -154,8 +154,8 @@ func _() int {
print(1)
{
panic(2)
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
}
@@ -164,8 +164,8 @@ func _() int {
print(1)
return 2
}
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
func _() int {
@@ -174,8 +174,8 @@ L:
print(1)
goto L
}
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
func _() int {
@@ -183,8 +183,8 @@ func _() int {
{
panic(2)
}
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
func _() int {
@@ -991,31 +991,31 @@ func _() int {
func _() int {
return 2
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
println() // ok
}
func _() int {
L:
goto L
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
println() // ok
}
func _() int {
panic(2)
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
println() // ok
}
func _() int {
{
return 2
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
println() // ok
}
@@ -1024,8 +1024,8 @@ func _() int {
L:
{
goto L
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
println() // ok
}
@@ -1033,8 +1033,8 @@ L:
func _() int {
{
panic(2)
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
println() // ok
}
@@ -1043,8 +1043,8 @@ func _() int {
{
return 2
}
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
println() // ok
}
@@ -1053,8 +1053,8 @@ L:
{
goto L
}
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
println() // ok
}
@@ -1062,8 +1062,8 @@ func _() int {
{
panic(2)
}
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
println() // ok
}
@@ -1165,31 +1165,31 @@ var _ = func() int {
var _ = func() int {
print(1)
return 2
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
var _ = func() int {
L:
print(1)
goto L
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
var _ = func() int {
print(1)
panic(2)
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
var _ = func() int {
{
print(1)
return 2
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
}
@@ -1198,8 +1198,8 @@ L:
{
print(1)
goto L
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
}
@@ -1207,8 +1207,8 @@ var _ = func() int {
print(1)
{
panic(2)
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
}
@@ -1217,8 +1217,8 @@ var _ = func() int {
print(1)
return 2
}
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
var _ = func() int {
@@ -1227,8 +1227,8 @@ L:
print(1)
goto L
}
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
var _ = func() int {
@@ -1236,8 +1236,8 @@ var _ = func() int {
{
panic(2)
}
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
var _ = func() int {
@@ -2044,31 +2044,31 @@ var _ = func() int {
var _ = func() int {
return 2
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
println() // ok
}
var _ = func() int {
L:
goto L
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
println() // ok
}
var _ = func() int {
panic(2)
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
println() // ok
}
var _ = func() int {
{
return 2
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
println() // ok
}
@@ -2077,8 +2077,8 @@ var _ = func() int {
L:
{
goto L
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
println() // ok
}
@@ -2086,8 +2086,8 @@ L:
var _ = func() int {
{
panic(2)
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
}
println() // ok
}
@@ -2096,8 +2096,8 @@ var _ = func() int {
{
return 2
}
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
println() // ok
}
@@ -2106,8 +2106,8 @@ L:
{
goto L
}
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
println() // ok
}
@@ -2115,7 +2115,7 @@ var _ = func() int {
{
panic(2)
}
- {
- } // ERROR "unreachable code"
+ { // ERROR "unreachable code"
+ }
println() // ok
}
diff --git a/src/lib9/goos.c b/src/lib9/goos.c
index 3b0027111..2d4a800dd 100644
--- a/src/lib9/goos.c
+++ b/src/lib9/goos.c
@@ -51,3 +51,9 @@ getgo386(void)
{
return defgetenv("GO386", GO386);
}
+
+char *
+getgoextlinkenabled(void)
+{
+ return GO_EXTLINK_ENABLED;
+}
diff --git a/src/lib9/utf/utf.h b/src/lib9/utf/utf.h
index 8a79828bc..72d01ed63 100644
--- a/src/lib9/utf/utf.h
+++ b/src/lib9/utf/utf.h
@@ -16,8 +16,6 @@
#ifndef _UTFH_
#define _UTFH_ 1
-#include <stdint.h>
-
typedef unsigned int Rune; /* Code-point values in Unicode 4.0 are 21 bits wide.*/
enum
diff --git a/src/libmach/sym.c b/src/libmach/sym.c
index 120328d09..28c80d641 100644
--- a/src/libmach/sym.c
+++ b/src/libmach/sym.c
@@ -109,7 +109,7 @@ int
syminit(int fd, Fhdr *fp)
{
Sym *p;
- int32 i, l, size;
+ int32 i, l, size, symsz;
vlong vl;
Biobuf b;
int svalsz, newformat, shift;
@@ -138,6 +138,7 @@ syminit(int fd, Fhdr *fp)
memset(buf, 0, sizeof buf);
Bread(&b, buf, sizeof buf);
newformat = 0;
+ symsz = fp->symsz;
if(memcmp(buf, "\xfd\xff\xff\xff\x00\x00\x00", 7) == 0) {
swav = leswav;
swal = leswal;
@@ -151,6 +152,7 @@ syminit(int fd, Fhdr *fp)
swav = leswav;
swal = leswal;
Bseek(&b, fp->symoff+6, 0);
+ symsz -= 6;
} else {
Bseek(&b, fp->symoff, 0);
}
@@ -161,11 +163,12 @@ syminit(int fd, Fhdr *fp)
werrstr("invalid word size %d bytes", svalsz);
return -1;
}
+ symsz -= 8;
}
nsym = 0;
size = 0;
- for(p = symbols; size < fp->symsz; p++, nsym++) {
+ for(p = symbols; size < symsz; p++, nsym++) {
if(newformat) {
// Go 1.1 format. See comment at top of ../pkg/runtime/symtab.c.
if(Bread(&b, &c, 1) != 1)
diff --git a/src/make.bash b/src/make.bash
index 2d83b6f4d..8d0f6ebae 100755
--- a/src/make.bash
+++ b/src/make.bash
@@ -30,6 +30,11 @@
# to include all cgo related files, .c and .go file with "cgo"
# build directive, in the build. Set it to 0 to ignore them.
#
+# GO_EXTLINK_ENABLED: Set to 1 to invoke the host linker when building
+# packages that use cgo. Set to 0 to do all linking internally. This
+# controls the default behavior of the linker's -linkmode option. The
+# default value depends on the system.
+#
# CC: Command line to run to get at host C compiler.
# Default is "gcc". Also supported: "clang".
diff --git a/src/make.bat b/src/make.bat
index be1c8f52d..c1f171de4 100644
--- a/src/make.bat
+++ b/src/make.bat
@@ -109,8 +109,8 @@ if x%1==x--no-banner goto nobanner
goto end
:copydist
-mkdir %GOTOOLDIR% 2>NUL
-copy cmd\dist\dist.exe %GOTOOLDIR%\
+mkdir "%GOTOOLDIR%" 2>NUL
+copy cmd\dist\dist.exe "%GOTOOLDIR%\"
goto end
:fail
diff --git a/src/pkg/bufio/scan.go b/src/pkg/bufio/scan.go
index d94f7f9ad..486853e6b 100644
--- a/src/pkg/bufio/scan.go
+++ b/src/pkg/bufio/scan.go
@@ -16,7 +16,7 @@ import (
// the Scan method will step through the 'tokens' of a file, skipping
// the bytes between the tokens. The specification of a token is
// defined by a split function of type SplitFunc; the default split
-// function breaks the input into lines with newlines stripped. Split
+// function breaks the input into lines with line termination stripped. Split
// functions are defined in this package for scanning a file into
// lines, bytes, UTF-8-encoded runes, and space-delimited words. The
// client may instead provide a custom split function.
@@ -70,6 +70,7 @@ const (
)
// NewScanner returns a new Scanner to read from r.
+// The split function defaults to ScanLines.
func NewScanner(r io.Reader) *Scanner {
return &Scanner{
r: r,
@@ -257,7 +258,7 @@ func dropCR(data []byte) []byte {
// ScanLines is a split function for a Scanner that returns each line of
// text, stripped of any trailing end-of-line marker. The returned line may
// be empty. The end-of-line marker is one optional carriage return followed
-// by one mandatory newline. In regular expression notation, it is `\r?\n'.
+// by one mandatory newline. In regular expression notation, it is `\r?\n`.
// The last non-empty line of input will be returned even if it has no
// newline.
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
diff --git a/src/pkg/builtin/builtin.go b/src/pkg/builtin/builtin.go
index 7b5e9ab1d..d3c1a5c55 100644
--- a/src/pkg/builtin/builtin.go
+++ b/src/pkg/builtin/builtin.go
@@ -85,6 +85,15 @@ type byte byte
// used, by convention, to distinguish character values from integer values.
type rune rune
+// iota is a predeclared identifier representing the untyped integer ordinal
+// number of the current const specification in a (usually parenthesized)
+// const declaration. It is zero-indexed.
+const iota = 0 // Untyped int.
+
+// nil is a predeclared identifier representing the zero value for a
+// pointer, channel, func, interface, map, or slice type.
+var nil Type // Type must be a pointer, channel, func, interface, map, or slice type
+
// Type is here for the purposes of documentation only. It is a stand-in
// for any Go type, but represents the same type for any given function
// invocation.
diff --git a/src/pkg/bytes/asm_386.s b/src/pkg/bytes/asm_386.s
index 997738fe2..27cd4e787 100644
--- a/src/pkg/bytes/asm_386.s
+++ b/src/pkg/bytes/asm_386.s
@@ -15,19 +15,3 @@ TEXT ·IndexByte(SB),7,$0
SUBL $1, DI
MOVL DI, ret+16(FP)
RET
-
-TEXT ·Equal(SB),7,$0
- MOVL a_len+4(FP), BX
- MOVL b_len+16(FP), CX
- MOVL $0, AX
- CMPL BX, CX
- JNE eqret
- MOVL a+0(FP), SI
- MOVL b+12(FP), DI
- CLD
- REP; CMPSB
- JNE eqret
- MOVL $1, AX
-eqret:
- MOVB AX, ret+24(FP)
- RET
diff --git a/src/pkg/bytes/asm_amd64.s b/src/pkg/bytes/asm_amd64.s
index b8f9f1b81..b84957b6d 100644
--- a/src/pkg/bytes/asm_amd64.s
+++ b/src/pkg/bytes/asm_amd64.s
@@ -89,20 +89,3 @@ success:
SUBL $1, DI
MOVQ DI, ret+32(FP)
RET
-
-TEXT ·Equal(SB),7,$0
- MOVQ a_len+8(FP), BX
- MOVQ b_len+32(FP), CX
- MOVL $0, AX
- CMPQ BX, CX
- JNE eqret
- MOVQ a+0(FP), SI
- MOVQ b+24(FP), DI
- CLD
- REP; CMPSB
- MOVL $1, DX
- CMOVLEQ DX, AX
-eqret:
- MOVB AX, ret+48(FP)
- RET
-
diff --git a/src/pkg/bytes/buffer.go b/src/pkg/bytes/buffer.go
index 85c157798..46ca1d5ad 100644
--- a/src/pkg/bytes/buffer.go
+++ b/src/pkg/bytes/buffer.go
@@ -87,6 +87,13 @@ func (b *Buffer) grow(n int) int {
var buf []byte
if b.buf == nil && n <= len(b.bootstrap) {
buf = b.bootstrap[0:]
+ } else if m+n <= cap(b.buf)/2 {
+ // We can slide things down instead of allocating a new
+ // slice. We only need m+n <= cap(b.buf) to slide, but
+ // we instead let capacity get twice as large so we
+ // don't spend all our time copying.
+ copy(b.buf[:], b.buf[b.off:])
+ buf = b.buf[:m]
} else {
// not enough space anywhere
buf = makeSlice(2*cap(b.buf) + n)
@@ -112,20 +119,18 @@ func (b *Buffer) Grow(n int) {
b.buf = b.buf[0:m]
}
-// Write appends the contents of p to the buffer. The return
-// value n is the length of p; err is always nil.
-// If the buffer becomes too large, Write will panic with
-// ErrTooLarge.
+// Write appends the contents of p to the buffer, growing the buffer as
+// needed. The return value n is the length of p; err is always nil. If the
+// buffer becomes too large, Write will panic with ErrTooLarge.
func (b *Buffer) Write(p []byte) (n int, err error) {
b.lastRead = opInvalid
m := b.grow(len(p))
return copy(b.buf[m:], p), nil
}
-// WriteString appends the contents of s to the buffer. The return
-// value n is the length of s; err is always nil.
-// If the buffer becomes too large, WriteString will panic with
-// ErrTooLarge.
+// WriteString appends the contents of s to the buffer, growing the buffer as
+// needed. The return value n is the length of s; err is always nil. If the
+// buffer becomes too large, WriteString will panic with ErrTooLarge.
func (b *Buffer) WriteString(s string) (n int, err error) {
b.lastRead = opInvalid
m := b.grow(len(s))
@@ -138,12 +143,10 @@ func (b *Buffer) WriteString(s string) (n int, err error) {
// underlying buffer.
const MinRead = 512
-// ReadFrom reads data from r until EOF and appends it to the buffer.
-// The return value n is the number of bytes read.
-// Any error except io.EOF encountered during the read
-// is also returned.
-// If the buffer becomes too large, ReadFrom will panic with
-// ErrTooLarge.
+// ReadFrom reads data from r until EOF and appends it to the buffer, growing
+// the buffer as needed. The return value n is the number of bytes read. Any
+// error except io.EOF encountered during the read is also returned. If the
+// buffer becomes too large, ReadFrom will panic with ErrTooLarge.
func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
b.lastRead = opInvalid
// If buffer is empty, reset to recover space.
@@ -188,10 +191,10 @@ func makeSlice(n int) []byte {
return make([]byte, n)
}
-// WriteTo writes data to w until the buffer is drained or an error
-// occurs. The return value n is the number of bytes written; it always
-// fits into an int, but it is int64 to match the io.WriterTo interface.
-// Any error encountered during the write is also returned.
+// WriteTo writes data to w until the buffer is drained or an error occurs.
+// The return value n is the number of bytes written; it always fits into an
+// int, but it is int64 to match the io.WriterTo interface. Any error
+// encountered during the write is also returned.
func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) {
b.lastRead = opInvalid
if b.off < len(b.buf) {
@@ -216,10 +219,9 @@ func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) {
return
}
-// WriteByte appends the byte c to the buffer.
-// The returned error is always nil, but is included
-// to match bufio.Writer's WriteByte.
-// If the buffer becomes too large, WriteByte will panic with
+// WriteByte appends the byte c to the buffer, growing the buffer as needed.
+// The returned error is always nil, but is included to match bufio.Writer's
+// WriteByte. If the buffer becomes too large, WriteByte will panic with
// ErrTooLarge.
func (b *Buffer) WriteByte(c byte) error {
b.lastRead = opInvalid
@@ -228,12 +230,10 @@ func (b *Buffer) WriteByte(c byte) error {
return nil
}
-// WriteRune appends the UTF-8 encoding of Unicode
-// code point r to the buffer, returning its length and
-// an error, which is always nil but is included
-// to match bufio.Writer's WriteRune.
-// If the buffer becomes too large, WriteRune will panic with
-// ErrTooLarge.
+// WriteRune appends the UTF-8 encoding of Unicode code point r to the
+// buffer, returning its length and an error, which is always nil but is
+// included to match bufio.Writer's WriteRune. The buffer is grown as needed;
+// if it becomes too large, WriteRune will panic with ErrTooLarge.
func (b *Buffer) WriteRune(r rune) (n int, err error) {
if r < utf8.RuneSelf {
b.WriteByte(byte(r))
diff --git a/src/pkg/bytes/buffer_test.go b/src/pkg/bytes/buffer_test.go
index f9fb2625a..75145b05e 100644
--- a/src/pkg/bytes/buffer_test.go
+++ b/src/pkg/bytes/buffer_test.go
@@ -475,3 +475,53 @@ func TestUnreadByte(t *testing.T) {
t.Errorf("ReadByte = %q; want %q", c, 'm')
}
}
+
+// Tests that we occasionally compact. Issue 5154.
+func TestBufferGrowth(t *testing.T) {
+ var b Buffer
+ buf := make([]byte, 1024)
+ b.Write(buf[0:1])
+ var cap0 int
+ for i := 0; i < 5<<10; i++ {
+ b.Write(buf)
+ b.Read(buf)
+ if i == 0 {
+ cap0 = b.Cap()
+ }
+ }
+ cap1 := b.Cap()
+ // (*Buffer).grow allows for 2x capacity slop before sliding,
+ // so set our error threshold at 3x.
+ if cap1 > cap0*3 {
+ t.Errorf("buffer cap = %d; too big (grew from %d)", cap1, cap0)
+ }
+}
+
+// From Issue 5154.
+func BenchmarkBufferNotEmptyWriteRead(b *testing.B) {
+ buf := make([]byte, 1024)
+ for i := 0; i < b.N; i++ {
+ var b Buffer
+ b.Write(buf[0:1])
+ for i := 0; i < 5<<10; i++ {
+ b.Write(buf)
+ b.Read(buf)
+ }
+ }
+}
+
+// Check that we don't compact too often. From Issue 5154.
+func BenchmarkBufferFullSmallReads(b *testing.B) {
+ buf := make([]byte, 1024)
+ for i := 0; i < b.N; i++ {
+ var b Buffer
+ b.Write(buf)
+ for b.Len()+20 < b.Cap() {
+ b.Write(buf[:10])
+ }
+ for i := 0; i < 5<<10; i++ {
+ b.Read(buf[:1])
+ b.Write(buf[:1])
+ }
+ }
+}
diff --git a/src/pkg/bytes/bytes_decl.go b/src/pkg/bytes/bytes_decl.go
index ce78be416..fbf928275 100644
--- a/src/pkg/bytes/bytes_decl.go
+++ b/src/pkg/bytes/bytes_decl.go
@@ -13,4 +13,4 @@ func IndexByte(s []byte, c byte) int // asm_$GOARCH.s
// Equal returns a boolean reporting whether a == b.
// A nil argument is equivalent to an empty slice.
-func Equal(a, b []byte) bool // asm_$GOARCH.s
+func Equal(a, b []byte) bool // asm_arm.s or ../runtime/asm_{386,amd64}.s
diff --git a/src/pkg/bytes/bytes_test.go b/src/pkg/bytes/bytes_test.go
index 1d6274c33..d296224ac 100644
--- a/src/pkg/bytes/bytes_test.go
+++ b/src/pkg/bytes/bytes_test.go
@@ -61,6 +61,10 @@ var compareTests = []struct {
{[]byte("ab"), []byte("x"), -1},
{[]byte("x"), []byte("a"), 1},
{[]byte("b"), []byte("x"), -1},
+ // test runtime·memeq's chunked implementation
+ {[]byte("abcdefgh"), []byte("abcdefgh"), 0},
+ {[]byte("abcdefghi"), []byte("abcdefghi"), 0},
+ {[]byte("abcdefghi"), []byte("abcdefghj"), -1},
// nil tests
{nil, nil, 0},
{[]byte(""), nil, 0},
@@ -86,6 +90,58 @@ func TestCompare(t *testing.T) {
}
}
+func TestEqual(t *testing.T) {
+ var size = 128
+ if testing.Short() {
+ size = 32
+ }
+ a := make([]byte, size)
+ b := make([]byte, size)
+ b_init := make([]byte, size)
+ // randomish but deterministic data
+ for i := 0; i < size; i++ {
+ a[i] = byte(17 * i)
+ b_init[i] = byte(23*i + 100)
+ }
+
+ for len := 0; len <= size; len++ {
+ for x := 0; x <= size-len; x++ {
+ for y := 0; y <= size-len; y++ {
+ copy(b, b_init)
+ copy(b[y:y+len], a[x:x+len])
+ if !Equal(a[x:x+len], b[y:y+len]) || !Equal(b[y:y+len], a[x:x+len]) {
+ t.Errorf("Equal(%d, %d, %d) = false", len, x, y)
+ }
+ }
+ }
+ }
+}
+
+// make sure Equal returns false for minimally different strings. The data
+// is all zeros except for a single one in one location.
+func TestNotEqual(t *testing.T) {
+ var size = 128
+ if testing.Short() {
+ size = 32
+ }
+ a := make([]byte, size)
+ b := make([]byte, size)
+
+ for len := 0; len <= size; len++ {
+ for x := 0; x <= size-len; x++ {
+ for y := 0; y <= size-len; y++ {
+ for diffpos := x; diffpos < x+len; diffpos++ {
+ a[diffpos] = 1
+ if Equal(a[x:x+len], b[y:y+len]) || Equal(b[y:y+len], a[x:x+len]) {
+ t.Errorf("NotEqual(%d, %d, %d, %d) = true", len, x, y, diffpos)
+ }
+ a[diffpos] = 0
+ }
+ }
+ }
+ }
+}
+
var indexTests = []BinOpTest{
{"", "", 0},
{"", "a", -1},
@@ -303,10 +359,30 @@ func bmIndexByte(b *testing.B, index func([]byte, byte) int, n int) {
buf[n-1] = '\x00'
}
+func BenchmarkEqual0(b *testing.B) {
+ var buf [4]byte
+ buf1 := buf[0:0]
+ buf2 := buf[1:1]
+ for i := 0; i < b.N; i++ {
+ eq := Equal(buf1, buf2)
+ if !eq {
+ b.Fatal("bad equal")
+ }
+ }
+}
+
+func BenchmarkEqual1(b *testing.B) { bmEqual(b, Equal, 1) }
+func BenchmarkEqual6(b *testing.B) { bmEqual(b, Equal, 6) }
+func BenchmarkEqual9(b *testing.B) { bmEqual(b, Equal, 9) }
+func BenchmarkEqual15(b *testing.B) { bmEqual(b, Equal, 15) }
+func BenchmarkEqual16(b *testing.B) { bmEqual(b, Equal, 16) }
+func BenchmarkEqual20(b *testing.B) { bmEqual(b, Equal, 20) }
func BenchmarkEqual32(b *testing.B) { bmEqual(b, Equal, 32) }
func BenchmarkEqual4K(b *testing.B) { bmEqual(b, Equal, 4<<10) }
func BenchmarkEqual4M(b *testing.B) { bmEqual(b, Equal, 4<<20) }
func BenchmarkEqual64M(b *testing.B) { bmEqual(b, Equal, 64<<20) }
+func BenchmarkEqualPort1(b *testing.B) { bmEqual(b, EqualPortable, 1) }
+func BenchmarkEqualPort6(b *testing.B) { bmEqual(b, EqualPortable, 6) }
func BenchmarkEqualPort32(b *testing.B) { bmEqual(b, EqualPortable, 32) }
func BenchmarkEqualPort4K(b *testing.B) { bmEqual(b, EqualPortable, 4<<10) }
func BenchmarkEqualPortable4M(b *testing.B) { bmEqual(b, EqualPortable, 4<<20) }
diff --git a/src/pkg/bytes/equal_test.go b/src/pkg/bytes/equal_test.go
new file mode 100644
index 000000000..1bf19a74b
--- /dev/null
+++ b/src/pkg/bytes/equal_test.go
@@ -0,0 +1,47 @@
+// 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 linux
+
+package bytes_test
+
+import (
+ . "bytes"
+ "syscall"
+ "testing"
+ "unsafe"
+)
+
+// This file tests the situation where memeq is checking
+// data very near to a page boundary. We want to make sure
+// equal does not read across the boundary and cause a page
+// fault where it shouldn't.
+
+// This test runs only on linux. The code being tested is
+// not OS-specific, so it does not need to be tested on all
+// operating systems.
+
+func TestEqualNearPageBoundary(t *testing.T) {
+ pagesize := syscall.Getpagesize()
+ b := make([]byte, 4*pagesize)
+ i := pagesize
+ for ; uintptr(unsafe.Pointer(&b[i]))%uintptr(pagesize) != 0; i++ {
+ }
+ syscall.Mprotect(b[i-pagesize:i], 0)
+ syscall.Mprotect(b[i+pagesize:i+2*pagesize], 0)
+ defer syscall.Mprotect(b[i-pagesize:i], syscall.PROT_READ|syscall.PROT_WRITE)
+ defer syscall.Mprotect(b[i+pagesize:i+2*pagesize], syscall.PROT_READ|syscall.PROT_WRITE)
+
+ // both of these should fault
+ //pagesize += int(b[i-1])
+ //pagesize += int(b[i+pagesize])
+
+ for j := 0; j < pagesize; j++ {
+ b[i+j] = 'A'
+ }
+ for j := 0; j <= pagesize; j++ {
+ Equal(b[i:i+j], b[i+pagesize-j:i+pagesize])
+ Equal(b[i+pagesize-j:i+pagesize], b[i:i+j])
+ }
+}
diff --git a/src/pkg/bytes/export_test.go b/src/pkg/bytes/export_test.go
index f61523e60..3b915d5ea 100644
--- a/src/pkg/bytes/export_test.go
+++ b/src/pkg/bytes/export_test.go
@@ -7,3 +7,7 @@ package bytes
// Export func for testing
var IndexBytePortable = indexBytePortable
var EqualPortable = equalPortable
+
+func (b *Buffer) Cap() int {
+ return cap(b.buf)
+}
diff --git a/src/pkg/compress/gzip/gzip.go b/src/pkg/compress/gzip/gzip.go
index 3035dfffc..45558b742 100644
--- a/src/pkg/compress/gzip/gzip.go
+++ b/src/pkg/compress/gzip/gzip.go
@@ -28,7 +28,7 @@ type Writer struct {
Header
w io.Writer
level int
- compressor io.WriteCloser
+ compressor *flate.Writer
digest hash.Hash32
size uint32
closed bool
@@ -191,6 +191,28 @@ func (z *Writer) Write(p []byte) (int, error) {
return n, z.err
}
+// Flush flushes any pending compressed data to the underlying writer.
+//
+// It is useful mainly in compressed network protocols, to ensure that
+// a remote reader has enough data to reconstruct a packet. Flush does
+// not return until the data has been written. If the underlying
+// writer returns an error, Flush returns that error.
+//
+// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH.
+func (z *Writer) Flush() error {
+ if z.err != nil {
+ return z.err
+ }
+ if z.closed {
+ return nil
+ }
+ if z.compressor == nil {
+ z.Write(nil)
+ }
+ z.err = z.compressor.Flush()
+ return z.err
+}
+
// Close closes the Writer. It does not close the underlying io.Writer.
func (z *Writer) Close() error {
if z.err != nil {
diff --git a/src/pkg/compress/gzip/gzip_test.go b/src/pkg/compress/gzip/gzip_test.go
index 6f7b59364..4d1af9438 100644
--- a/src/pkg/compress/gzip/gzip_test.go
+++ b/src/pkg/compress/gzip/gzip_test.go
@@ -157,3 +157,43 @@ func TestLatin1RoundTrip(t *testing.T) {
}
}
}
+
+func TestWriterFlush(t *testing.T) {
+ buf := new(bytes.Buffer)
+
+ w := NewWriter(buf)
+ w.Comment = "comment"
+ w.Extra = []byte("extra")
+ w.ModTime = time.Unix(1e8, 0)
+ w.Name = "name"
+
+ n0 := buf.Len()
+ if n0 != 0 {
+ t.Fatalf("buffer size = %d before writes; want 0", n0)
+ }
+
+ if err := w.Flush(); err != nil {
+ t.Fatal(err)
+ }
+
+ n1 := buf.Len()
+ if n1 == 0 {
+ t.Fatal("no data after first flush")
+ }
+
+ w.Write([]byte("x"))
+
+ n2 := buf.Len()
+ if n1 != n2 {
+ t.Fatalf("after writing a single byte, size changed from %d to %d; want no change", n1, n2)
+ }
+
+ if err := w.Flush(); err != nil {
+ t.Fatal(err)
+ }
+
+ n3 := buf.Len()
+ if n2 == n3 {
+ t.Fatal("Flush didn't flush any data")
+ }
+}
diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go
index 35a5f7c3c..f56fb37ee 100644
--- a/src/pkg/crypto/rsa/rsa.go
+++ b/src/pkg/crypto/rsa/rsa.go
@@ -203,7 +203,9 @@ NextSetOfPrimes:
g.GCD(priv.D, y, e, totient)
if g.Cmp(bigOne) == 0 {
- priv.D.Add(priv.D, totient)
+ if priv.D.Sign() < 0 {
+ priv.D.Add(priv.D, totient)
+ }
priv.Primes = primes
priv.N = n
diff --git a/src/pkg/crypto/rsa/rsa_test.go b/src/pkg/crypto/rsa/rsa_test.go
index f08cfe73c..ffd96e62f 100644
--- a/src/pkg/crypto/rsa/rsa_test.go
+++ b/src/pkg/crypto/rsa/rsa_test.go
@@ -93,6 +93,9 @@ func testKeyBasics(t *testing.T, priv *PrivateKey) {
if err := priv.Validate(); err != nil {
t.Errorf("Validate() failed: %s", err)
}
+ if priv.D.Cmp(priv.N) > 0 {
+ t.Errorf("private exponent too large")
+ }
pub := &priv.PublicKey
m := big.NewInt(42)
diff --git a/src/pkg/database/sql/convert.go b/src/pkg/database/sql/convert.go
index 5530a5d90..c04adde1f 100644
--- a/src/pkg/database/sql/convert.go
+++ b/src/pkg/database/sql/convert.go
@@ -112,15 +112,13 @@ func convertAssign(dest, src interface{}) error {
if d == nil {
return errNilPtr
}
- bcopy := make([]byte, len(s))
- copy(bcopy, s)
- *d = bcopy
+ *d = cloneBytes(s)
return nil
case *[]byte:
if d == nil {
return errNilPtr
}
- *d = s
+ *d = cloneBytes(s)
return nil
case *RawBytes:
if d == nil {
@@ -131,6 +129,12 @@ func convertAssign(dest, src interface{}) error {
}
case nil:
switch d := dest.(type) {
+ case *interface{}:
+ if d == nil {
+ return errNilPtr
+ }
+ *d = nil
+ return nil
case *[]byte:
if d == nil {
return errNilPtr
@@ -250,6 +254,16 @@ func convertAssign(dest, src interface{}) error {
return fmt.Errorf("unsupported driver -> Scan pair: %T -> %T", src, dest)
}
+func cloneBytes(b []byte) []byte {
+ if b == nil {
+ return nil
+ } else {
+ c := make([]byte, len(b))
+ copy(c, b)
+ return c
+ }
+}
+
func asString(src interface{}) string {
switch v := src.(type) {
case string:
diff --git a/src/pkg/database/sql/convert_test.go b/src/pkg/database/sql/convert_test.go
index 6aedeb0a4..950e24fc3 100644
--- a/src/pkg/database/sql/convert_test.go
+++ b/src/pkg/database/sql/convert_test.go
@@ -143,6 +143,7 @@ var conversionTests = []conversionTest{
{s: []byte("byteslice"), d: &scaniface, wantiface: []byte("byteslice")},
{s: true, d: &scaniface, wantiface: true},
{s: nil, d: &scaniface},
+ {s: []byte(nil), d: &scaniface, wantiface: []byte(nil)},
}
func intPtrValue(intptr interface{}) interface{} {
@@ -221,7 +222,7 @@ func TestConversions(t *testing.T) {
}
if srcBytes, ok := ct.s.([]byte); ok {
dstBytes := (*ifptr).([]byte)
- if &dstBytes[0] == &srcBytes[0] {
+ if len(srcBytes) > 0 && &dstBytes[0] == &srcBytes[0] {
errf("copy into interface{} didn't copy []byte data")
}
}
diff --git a/src/pkg/database/sql/fakedb_test.go b/src/pkg/database/sql/fakedb_test.go
index 55597f7de..24c255f6e 100644
--- a/src/pkg/database/sql/fakedb_test.go
+++ b/src/pkg/database/sql/fakedb_test.go
@@ -229,7 +229,26 @@ func (c *fakeConn) Begin() (driver.Tx, error) {
return c.currTx, nil
}
-func (c *fakeConn) Close() error {
+var hookPostCloseConn struct {
+ sync.Mutex
+ fn func(*fakeConn, error)
+}
+
+func setHookpostCloseConn(fn func(*fakeConn, error)) {
+ hookPostCloseConn.Lock()
+ defer hookPostCloseConn.Unlock()
+ hookPostCloseConn.fn = fn
+}
+
+func (c *fakeConn) Close() (err error) {
+ defer func() {
+ hookPostCloseConn.Lock()
+ fn := hookPostCloseConn.fn
+ hookPostCloseConn.Unlock()
+ if fn != nil {
+ fn(c, err)
+ }
+ }()
if c.currTx != nil {
return errors.New("can't close fakeConn; in a Transaction")
}
diff --git a/src/pkg/database/sql/sql.go b/src/pkg/database/sql/sql.go
index bc92ecd8e..bd450c7ec 100644
--- a/src/pkg/database/sql/sql.go
+++ b/src/pkg/database/sql/sql.go
@@ -4,6 +4,9 @@
// Package sql provides a generic interface around SQL (or SQL-like)
// databases.
+//
+// The sql package must be used in conjunction with a database driver.
+// See http://golang.org/s/sqldrivers for a list of drivers.
package sql
import (
@@ -189,14 +192,12 @@ type DB struct {
driver driver.Driver
dsn string
- mu sync.Mutex // protects following fields
- outConn map[*driverConn]bool // whether the conn is in use
- freeConn []*driverConn
- closed bool
- dep map[finalCloser]depSet
- onConnPut map[*driverConn][]func() // code (with mu held) run when conn is next returned
- lastPut map[*driverConn]string // stacktrace of last conn's put; debug only
- maxIdle int // zero means defaultMaxIdleConns; negative means 0
+ mu sync.Mutex // protects following fields
+ freeConn []*driverConn
+ closed bool
+ dep map[finalCloser]depSet
+ lastPut map[*driverConn]string // stacktrace of last conn's put; debug only
+ maxIdle int // zero means defaultMaxIdleConns; negative means 0
}
// driverConn wraps a driver.Conn with a mutex, to
@@ -204,8 +205,46 @@ type DB struct {
// interfaces returned via that Conn, such as calls on Tx, Stmt,
// Result, Rows)
type driverConn struct {
- sync.Mutex
- ci driver.Conn
+ db *DB
+
+ sync.Mutex // guards following
+ ci driver.Conn
+ closed bool
+
+ // guarded by db.mu
+ inUse bool
+ onPut []func() // code (with db.mu held) run when conn is next returned
+}
+
+// the dc.db's Mutex is held.
+func (dc *driverConn) closeDBLocked() error {
+ dc.Lock()
+ if dc.closed {
+ dc.Unlock()
+ return errors.New("sql: duplicate driverConn close")
+ }
+ dc.closed = true
+ dc.Unlock() // not defer; removeDep finalClose calls may need to lock
+ return dc.db.removeDepLocked(dc, dc)()
+}
+
+func (dc *driverConn) Close() error {
+ dc.Lock()
+ if dc.closed {
+ dc.Unlock()
+ return errors.New("sql: duplicate driverConn close")
+ }
+ dc.closed = true
+ dc.Unlock() // not defer; removeDep finalClose calls may need to lock
+ return dc.db.removeDep(dc, dc)
+}
+
+func (dc *driverConn) finalClose() error {
+ dc.Lock()
+ err := dc.ci.Close()
+ dc.ci = nil
+ dc.Unlock()
+ return err
}
// driverStmt associates a driver.Stmt with the
@@ -238,6 +277,10 @@ func (db *DB) addDep(x finalCloser, dep interface{}) {
//println(fmt.Sprintf("addDep(%T %p, %T %p)", x, x, dep, dep))
db.mu.Lock()
defer db.mu.Unlock()
+ db.addDepLocked(x, dep)
+}
+
+func (db *DB) addDepLocked(x finalCloser, dep interface{}) {
if db.dep == nil {
db.dep = make(map[finalCloser]depSet)
}
@@ -254,10 +297,16 @@ func (db *DB) addDep(x finalCloser, dep interface{}) {
// If x no longer has any dependencies, its finalClose method will be
// called and its error value will be returned.
func (db *DB) removeDep(x finalCloser, dep interface{}) error {
+ db.mu.Lock()
+ fn := db.removeDepLocked(x, dep)
+ db.mu.Unlock()
+ return fn()
+}
+
+func (db *DB) removeDepLocked(x finalCloser, dep interface{}) func() error {
//println(fmt.Sprintf("removeDep(%T %p, %T %p)", x, x, dep, dep))
done := false
- db.mu.Lock()
xdep := db.dep[x]
if xdep != nil {
delete(xdep, dep)
@@ -266,13 +315,14 @@ func (db *DB) removeDep(x finalCloser, dep interface{}) error {
done = true
}
}
- db.mu.Unlock()
if !done {
- return nil
+ return func() error { return nil }
+ }
+ return func() error {
+ //println(fmt.Sprintf("calling final close on %T %v (%#v)", x, x, x))
+ return x.finalClose()
}
- //println(fmt.Sprintf("calling final close on %T %v (%#v)", x, x, x))
- return x.finalClose()
}
// Open opens a database specified by its database driver name and a
@@ -280,7 +330,9 @@ func (db *DB) removeDep(x finalCloser, dep interface{}) error {
// database name and connection information.
//
// Most users will open a database via a driver-specific connection
-// helper function that returns a *DB.
+// helper function that returns a *DB. No database drivers are included
+// in the Go standard library. See http://golang.org/s/sqldrivers for
+// a list of third-party drivers.
//
// Open may just validate its arguments without creating a connection
// to the database. To verify that the data source name is valid, call
@@ -291,11 +343,9 @@ func Open(driverName, dataSourceName string) (*DB, error) {
return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
}
db := &DB{
- driver: driveri,
- dsn: dataSourceName,
- outConn: make(map[*driverConn]bool),
- lastPut: make(map[*driverConn]string),
- onConnPut: make(map[*driverConn][]func()),
+ driver: driveri,
+ dsn: dataSourceName,
+ lastPut: make(map[*driverConn]string),
}
return db, nil
}
@@ -320,9 +370,7 @@ func (db *DB) Close() error {
defer db.mu.Unlock()
var err error
for _, dc := range db.freeConn {
- dc.Lock()
- err1 := dc.ci.Close()
- dc.Unlock()
+ err1 := dc.closeDBLocked()
if err1 != nil {
err = err1
}
@@ -365,11 +413,7 @@ func (db *DB) SetMaxIdleConns(n int) {
dc := db.freeConn[nfree-1]
db.freeConn[nfree-1] = nil
db.freeConn = db.freeConn[:nfree-1]
- go func() {
- dc.Lock()
- dc.ci.Close()
- dc.Unlock()
- }()
+ go dc.Close()
}
}
@@ -383,7 +427,7 @@ func (db *DB) conn() (*driverConn, error) {
if n := len(db.freeConn); n > 0 {
conn := db.freeConn[n-1]
db.freeConn = db.freeConn[:n-1]
- db.outConn[conn] = true
+ conn.inUse = true
db.mu.Unlock()
return conn, nil
}
@@ -393,9 +437,13 @@ func (db *DB) conn() (*driverConn, error) {
if err != nil {
return nil, err
}
- dc := &driverConn{ci: ci}
+ dc := &driverConn{
+ db: db,
+ ci: ci,
+ }
db.mu.Lock()
- db.outConn[dc] = true
+ db.addDepLocked(dc, dc)
+ dc.inUse = true
db.mu.Unlock()
return dc, nil
}
@@ -408,7 +456,7 @@ func (db *DB) conn() (*driverConn, error) {
func (db *DB) connIfFree(wanted *driverConn) (conn *driverConn, ok bool) {
db.mu.Lock()
defer db.mu.Unlock()
- if db.outConn[wanted] {
+ if wanted.inUse {
return conn, false
}
for i, conn := range db.freeConn {
@@ -417,7 +465,7 @@ func (db *DB) connIfFree(wanted *driverConn) (conn *driverConn, ok bool) {
}
db.freeConn[i] = db.freeConn[len(db.freeConn)-1]
db.freeConn = db.freeConn[:len(db.freeConn)-1]
- db.outConn[wanted] = true
+ wanted.inUse = true
return wanted, true
}
return nil, false
@@ -432,8 +480,8 @@ var putConnHook func(*DB, *driverConn)
func (db *DB) noteUnusedDriverStatement(c *driverConn, si driver.Stmt) {
db.mu.Lock()
defer db.mu.Unlock()
- if db.outConn[c] {
- db.onConnPut[c] = append(db.onConnPut[c], func() {
+ if c.inUse {
+ c.onPut = append(c.onPut, func() {
si.Close()
})
} else {
@@ -449,7 +497,7 @@ const debugGetPut = false
// err is optionally the last error that occurred on this connection.
func (db *DB) putConn(dc *driverConn, err error) {
db.mu.Lock()
- if !db.outConn[dc] {
+ if !dc.inUse {
if debugGetPut {
fmt.Printf("putConn(%v) DUPLICATE was: %s\n\nPREVIOUS was: %s", dc, stack(), db.lastPut[dc])
}
@@ -458,14 +506,12 @@ func (db *DB) putConn(dc *driverConn, err error) {
if debugGetPut {
db.lastPut[dc] = stack()
}
- delete(db.outConn, dc)
+ dc.inUse = false
- if fns, ok := db.onConnPut[dc]; ok {
- for _, fn := range fns {
- fn()
- }
- delete(db.onConnPut, dc)
+ for _, fn := range dc.onPut {
+ fn()
}
+ dc.onPut = nil
if err == driver.ErrBadConn {
// Don't reuse bad connections.
@@ -484,9 +530,7 @@ func (db *DB) putConn(dc *driverConn, err error) {
// statements which are still active?
db.mu.Unlock()
- dc.Lock()
- dc.ci.Close()
- dc.Unlock()
+ dc.Close()
}
// Prepare creates a prepared statement for later queries or executions.
@@ -528,6 +572,7 @@ func (db *DB) prepare(query string) (*Stmt, error) {
css: []connStmt{{dc, si}},
}
db.addDep(stmt, stmt)
+ db.addDep(dc, stmt)
db.putConn(dc, nil)
return stmt, nil
}
@@ -1031,6 +1076,7 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St
if err != nil {
return nil, nil, nil, err
}
+ s.db.addDep(dc, s)
s.mu.Lock()
cs = connStmt{dc, si}
s.css = append(s.css, cs)
@@ -1149,6 +1195,7 @@ func (s *Stmt) Close() error {
func (s *Stmt) finalClose() error {
for _, v := range s.css {
s.db.noteUnusedDriverStatement(v.dc, v.si)
+ s.db.removeDep(v.dc, s)
}
s.css = nil
return nil
@@ -1252,24 +1299,6 @@ func (rs *Rows) Scan(dest ...interface{}) error {
return fmt.Errorf("sql: Scan error on column index %d: %v", i, err)
}
}
- for _, dp := range dest {
- b, ok := dp.(*[]byte)
- if !ok {
- continue
- }
- if *b == nil {
- // If the []byte is now nil (for a NULL value),
- // don't fall through to below which would
- // turn it into a non-nil 0-length byte slice
- continue
- }
- if _, ok = dp.(*RawBytes); ok {
- continue
- }
- clone := make([]byte, len(*b))
- copy(clone, *b)
- *b = clone
- }
return nil
}
diff --git a/src/pkg/database/sql/sql_test.go b/src/pkg/database/sql/sql_test.go
index 2a9592e10..6b9178378 100644
--- a/src/pkg/database/sql/sql_test.go
+++ b/src/pkg/database/sql/sql_test.go
@@ -7,7 +7,9 @@ package sql
import (
"fmt"
"reflect"
+ "runtime"
"strings"
+ "sync"
"testing"
"time"
)
@@ -36,7 +38,14 @@ const fakeDBName = "foo"
var chrisBirthday = time.Unix(123456789, 0)
-func newTestDB(t *testing.T, name string) *DB {
+type testOrBench interface {
+ Fatalf(string, ...interface{})
+ Errorf(string, ...interface{})
+ Fatal(...interface{})
+ Error(...interface{})
+}
+
+func newTestDB(t testOrBench, name string) *DB {
db, err := Open("test", fakeDBName)
if err != nil {
t.Fatalf("Open: %v", err)
@@ -53,18 +62,24 @@ func newTestDB(t *testing.T, name string) *DB {
return db
}
-func exec(t *testing.T, db *DB, query string, args ...interface{}) {
+func exec(t testOrBench, db *DB, query string, args ...interface{}) {
_, err := db.Exec(query, args...)
if err != nil {
t.Fatalf("Exec of %q: %v", query, err)
}
}
-func closeDB(t *testing.T, db *DB) {
+func closeDB(t testOrBench, db *DB) {
if e := recover(); e != nil {
fmt.Printf("Panic: %v\n", e)
panic(e)
}
+ defer setHookpostCloseConn(nil)
+ setHookpostCloseConn(func(_ *fakeConn, err error) {
+ if err != nil {
+ t.Errorf("Error closing fakeConn: %v", err)
+ }
+ })
err := db.Close()
if err != nil {
t.Fatalf("error closing DB: %v", err)
@@ -790,3 +805,111 @@ func TestMaxIdleConns(t *testing.T) {
t.Errorf("freeConns = %d; want 0", got)
}
}
+
+// golang.org/issue/5046
+func TestCloseConnBeforeStmts(t *testing.T) {
+ defer setHookpostCloseConn(nil)
+ setHookpostCloseConn(func(_ *fakeConn, err error) {
+ if err != nil {
+ t.Errorf("Error closing fakeConn: %v", err)
+ }
+ })
+
+ db := newTestDB(t, "people")
+
+ stmt, err := db.Prepare("SELECT|people|name|")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(db.freeConn) != 1 {
+ t.Fatalf("expected 1 freeConn; got %d", len(db.freeConn))
+ }
+ dc := db.freeConn[0]
+ if dc.closed {
+ t.Errorf("conn shouldn't be closed")
+ }
+
+ err = db.Close()
+ if err != nil {
+ t.Errorf("db Close = %v", err)
+ }
+ if !dc.closed {
+ t.Errorf("after db.Close, driverConn should be closed")
+ }
+ if dc.ci == nil {
+ t.Errorf("after db.Close, driverConn should still have its Conn interface")
+ }
+
+ err = stmt.Close()
+ if err != nil {
+ t.Errorf("Stmt close = %v", err)
+ }
+
+ if !dc.closed {
+ t.Errorf("conn should be closed")
+ }
+ if dc.ci != nil {
+ t.Errorf("after Stmt Close, driverConn's Conn interface should be nil")
+ }
+}
+
+func manyConcurrentQueries(t testOrBench) {
+ maxProcs, numReqs := 16, 500
+ if testing.Short() {
+ maxProcs, numReqs = 4, 50
+ }
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs))
+
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+
+ stmt, err := db.Prepare("SELECT|people|name|")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var wg sync.WaitGroup
+ wg.Add(numReqs)
+
+ reqs := make(chan bool)
+ defer close(reqs)
+
+ for i := 0; i < maxProcs*2; i++ {
+ go func() {
+ for _ = range reqs {
+ rows, err := stmt.Query()
+ if err != nil {
+ t.Errorf("error on query: %v", err)
+ wg.Done()
+ continue
+ }
+
+ var name string
+ for rows.Next() {
+ rows.Scan(&name)
+ }
+ rows.Close()
+
+ wg.Done()
+ }
+ }()
+ }
+
+ for i := 0; i < numReqs; i++ {
+ reqs <- true
+ }
+
+ wg.Wait()
+}
+
+func TestConcurrency(t *testing.T) {
+ manyConcurrentQueries(t)
+}
+
+func BenchmarkConcurrency(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ manyConcurrentQueries(b)
+ }
+}
diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go
index dc3669c1d..cc89afb21 100644
--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -301,7 +301,13 @@ func defaultContext() Context {
case "0":
c.CgoEnabled = false
default:
- c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]
+ // golang.org/issue/5141
+ // cgo should be disabled for cross compilation builds
+ if runtime.GOARCH == c.GOARCH && runtime.GOOS == c.GOOS {
+ c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]
+ break
+ }
+ c.CgoEnabled = false
}
return c
diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go
index 96d867cae..4264940a0 100644
--- a/src/pkg/go/doc/doc.go
+++ b/src/pkg/go/doc/doc.go
@@ -64,14 +64,14 @@ type Func struct {
Level int // embedding level; 0 means not embedded
}
-// A Note represents marked comments starting with "MARKER(uid): note body".
+// A Note represents a marked comment starting with "MARKER(uid): note body".
// Any note with a marker of 2 or more upper case [A-Z] letters and a uid of
// at least one character is recognized. The ":" following the uid is optional.
// Notes are collected in the Package.Notes map indexed by the notes marker.
type Note struct {
- Pos token.Pos // position of the comment containing the marker
- UID string // uid found with the marker
- Body string // note body text
+ Pos, End token.Pos // position range of the comment containing the marker
+ UID string // uid found with the marker
+ Body string // note body text
}
// Mode values control the operation of New.
diff --git a/src/pkg/go/doc/reader.go b/src/pkg/go/doc/reader.go
index 7e1422d0c..4fa6fd9d5 100644
--- a/src/pkg/go/doc/reader.go
+++ b/src/pkg/go/doc/reader.go
@@ -419,6 +419,7 @@ func (r *reader) readNote(list []*ast.Comment) {
marker := text[m[2]:m[3]]
r.notes[marker] = append(r.notes[marker], &Note{
Pos: list[0].Pos(),
+ End: list[len(list)-1].End(),
UID: text[m[4]:m[5]],
Body: body,
})
diff --git a/src/pkg/go/parser/interface.go b/src/pkg/go/parser/interface.go
index 39affdd6b..149257ca6 100644
--- a/src/pkg/go/parser/interface.go
+++ b/src/pkg/go/parser/interface.go
@@ -162,16 +162,27 @@ func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, m
}
// ParseExpr is a convenience function for obtaining the AST of an expression x.
-// The position information recorded in the AST is undefined.
+// The position information recorded in the AST is undefined. The filename used
+// in error messages is the empty string.
//
func ParseExpr(x string) (ast.Expr, error) {
- // parse x within the context of a complete package for correct scopes;
- // use //line directive for correct positions in error messages and put
- // x alone on a separate line (handles line comments), followed by a ';'
- // to force an error if the expression is incomplete
- file, err := ParseFile(token.NewFileSet(), "", "package p;func _(){_=\n//line :1\n"+x+"\n;}", 0)
- if err != nil {
- return nil, err
+ var p parser
+ p.init(token.NewFileSet(), "", []byte(x), 0)
+
+ // Set up pkg-level scopes to avoid nil-pointer errors.
+ // This is not needed for a correct expression x as the
+ // parser will be ok with a nil topScope, but be cautious
+ // in case of an erroneous x.
+ p.openScope()
+ p.pkgScope = p.topScope
+ e := p.parseRhsOrType()
+ p.closeScope()
+ assert(p.topScope == nil, "unbalanced scopes")
+
+ if p.errors.Len() > 0 {
+ p.errors.Sort()
+ return nil, p.errors.Err()
}
- return file.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.AssignStmt).Rhs[0], nil
+
+ return e, nil
}
diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go
index 1960377b0..48813d106 100644
--- a/src/pkg/go/parser/parser_test.go
+++ b/src/pkg/go/parser/parser_test.go
@@ -68,7 +68,7 @@ func TestParseDir(t *testing.T) {
func TestParseExpr(t *testing.T) {
// just kicking the tires:
- // a valid expression
+ // a valid arithmetic expression
src := "a + b"
x, err := ParseExpr(src)
if err != nil {
@@ -79,6 +79,17 @@ func TestParseExpr(t *testing.T) {
t.Errorf("ParseExpr(%s): got %T, expected *ast.BinaryExpr", src, x)
}
+ // a valid type expression
+ src = "struct{x *int}"
+ x, err = ParseExpr(src)
+ if err != nil {
+ t.Fatalf("ParseExpr(%s): %v", src, err)
+ }
+ // sanity check
+ if _, ok := x.(*ast.StructType); !ok {
+ t.Errorf("ParseExpr(%s): got %T, expected *ast.StructType", src, x)
+ }
+
// an invalid expression
src = "a + *"
_, err = ParseExpr(src)
diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go
index 3c8d23e65..e06d2edfb 100644
--- a/src/pkg/go/printer/printer.go
+++ b/src/pkg/go/printer/printer.go
@@ -395,35 +395,6 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
}
}
-// Split comment text into lines
-// (using strings.Split(text, "\n") is significantly slower for
-// this specific purpose, as measured with: go test -bench=Print)
-//
-func split(text string) []string {
- // count lines (comment text never ends in a newline)
- n := 1
- for i := 0; i < len(text); i++ {
- if text[i] == '\n' {
- n++
- }
- }
-
- // split
- lines := make([]string, n)
- n = 0
- i := 0
- for j := 0; j < len(text); j++ {
- if text[j] == '\n' {
- lines[n] = text[i:j] // exclude newline
- i = j + 1 // discard newline
- n++
- }
- }
- lines[n] = text[i:]
-
- return lines
-}
-
// Returns true if s contains only white space
// (only tabs and blanks can appear in the printer's context).
//
@@ -616,7 +587,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
// for /*-style comments, print line by line and let the
// write function take care of the proper indentation
- lines := split(text)
+ lines := strings.Split(text, "\n")
// The comment started in the first column but is going
// to be indented. For an idempotent result, add indentation
diff --git a/src/pkg/image/gif/reader_test.go b/src/pkg/image/gif/reader_test.go
index a035ef1ea..dcc6c6dd3 100644
--- a/src/pkg/image/gif/reader_test.go
+++ b/src/pkg/image/gif/reader_test.go
@@ -114,22 +114,25 @@ func try(t *testing.T, b []byte, want string) {
}
func TestBounds(t *testing.T) {
+ // make a local copy of testGIF
+ gif := make([]byte, len(testGIF))
+ copy(gif, testGIF)
// Make the bounds too big, just by one.
- testGIF[32] = 2
+ gif[32] = 2
want := "gif: frame bounds larger than image bounds"
- try(t, testGIF, want)
+ try(t, gif, want)
// Make the bounds too small; does not trigger bounds
// check, but now there's too much data.
- testGIF[32] = 0
+ gif[32] = 0
want = "gif: too much image data"
- try(t, testGIF, want)
- testGIF[32] = 1
+ try(t, gif, want)
+ gif[32] = 1
// Make the bounds really big, expect an error.
want = "gif: frame bounds larger than image bounds"
for i := 0; i < 4; i++ {
- testGIF[32+i] = 0xff
+ gif[32+i] = 0xff
}
- try(t, testGIF, want)
+ try(t, gif, want)
}
diff --git a/src/pkg/math/big/int.go b/src/pkg/math/big/int.go
index fd7f005c2..d1b5602d6 100644
--- a/src/pkg/math/big/int.go
+++ b/src/pkg/math/big/int.go
@@ -53,7 +53,7 @@ func (z *Int) SetInt64(x int64) *Int {
// SetUint64 sets z to x and returns z.
func (z *Int) SetUint64(x uint64) *Int {
- z.abs = z.abs.setUint64(uint64(x))
+ z.abs = z.abs.setUint64(x)
z.neg = false
return z
}
@@ -513,13 +513,7 @@ func (z *Int) Scan(s fmt.ScanState, ch rune) error {
// Int64 returns the int64 representation of x.
// If x cannot be represented in an int64, the result is undefined.
func (x *Int) Int64() int64 {
- if len(x.abs) == 0 {
- return 0
- }
- v := int64(x.abs[0])
- if _W == 32 && len(x.abs) > 1 {
- v |= int64(x.abs[1]) << 32
- }
+ v := int64(x.Uint64())
if x.neg {
v = -v
}
@@ -527,7 +521,7 @@ func (x *Int) Int64() int64 {
}
// Uint64 returns the uint64 representation of x.
-// If x cannot be represented in an uint64, the result is undefined.
+// If x cannot be represented in a uint64, the result is undefined.
func (x *Int) Uint64() uint64 {
if len(x.abs) == 0 {
return 0
diff --git a/src/pkg/math/big/rat.go b/src/pkg/math/big/rat.go
index 3e6473d92..75d044fe2 100644
--- a/src/pkg/math/big/rat.go
+++ b/src/pkg/math/big/rat.go
@@ -163,16 +163,16 @@ func quotToFloat(a, b nat) (f float64, exact bool) {
return
}
-// Float64 returns the nearest float64 value to z.
-// If z is exactly representable as a float64, Float64 returns exact=true.
-// If z is negative, so too is f, even if f==0.
-func (z *Rat) Float64() (f float64, exact bool) {
- b := z.b.abs
+// Float64 returns the nearest float64 value for x and a bool indicating
+// whether f represents x exactly. The sign of f always matches the sign
+// of x, even if f == 0.
+func (x *Rat) Float64() (f float64, exact bool) {
+ b := x.b.abs
if len(b) == 0 {
b = b.set(natOne) // materialize denominator
}
- f, exact = quotToFloat(z.a.abs, b)
- if z.a.neg {
+ f, exact = quotToFloat(x.a.abs, b)
+ if x.a.neg {
f = -f
}
return
diff --git a/src/pkg/math/big/rat_test.go b/src/pkg/math/big/rat_test.go
index 462dfb723..1c2c64237 100644
--- a/src/pkg/math/big/rat_test.go
+++ b/src/pkg/math/big/rat_test.go
@@ -503,9 +503,7 @@ func TestIssue3521(t *testing.T) {
// Test inputs to Rat.SetString. The prefix "long:" causes the test
// to be skipped in --test.short mode. (The threshold is about 500us.)
var float64inputs = []string{
- //
// Constants plundered from strconv/testfp.txt.
- //
// Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP
"5e+125",
@@ -583,9 +581,7 @@ var float64inputs = []string{
"75224575729e-45",
"459926601011e+15",
- //
// Constants plundered from strconv/atof_test.go.
- //
"0",
"1",
@@ -734,7 +730,7 @@ func TestFloat64SpecialCases(t *testing.T) {
case f == 0 && r.Num().BitLen() == 0:
// Ok: Rat(0) is equivalent to both +/- float64(0).
default:
- t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta=%g", input, e, e, f, f, f-e)
+ t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e)
}
}
@@ -795,7 +791,7 @@ func TestFloat64Distribution(t *testing.T) {
if !checkIsBestApprox(t, f, r) {
// Append context information.
- t.Errorf("(input was mantissa %#x, exp %d; f=%g (%b); f~%g; r=%v)",
+ t.Errorf("(input was mantissa %#x, exp %d; f = %g (%b); f ~ %g; r = %v)",
b, exp, f, f, math.Ldexp(float64(b), exp), r)
}
@@ -830,7 +826,7 @@ func checkNonLossyRoundtrip(t *testing.T, f float64) {
}
f2, exact := r.Float64()
if f != f2 || !exact {
- t.Errorf("Rat.SetFloat64(%g).Float64() = %g (%b), %v, want %g (%b), %v; delta=%b",
+ t.Errorf("Rat.SetFloat64(%g).Float64() = %g (%b), %v, want %g (%b), %v; delta = %b",
f, f2, f2, exact, f, f, true, f2-f)
}
}
diff --git a/src/pkg/math/hypot.go b/src/pkg/math/hypot.go
index 3846e6d87..2087cb05b 100644
--- a/src/pkg/math/hypot.go
+++ b/src/pkg/math/hypot.go
@@ -12,8 +12,10 @@ package math
// unnecessary overflow and underflow.
//
// Special cases are:
-// Hypot(p, q) = +Inf if p or q is infinite
-// Hypot(p, q) = NaN if p or q is NaN
+// Hypot(±Inf, q) = +Inf
+// Hypot(p, ±Inf) = +Inf
+// Hypot(NaN, q) = NaN
+// Hypot(p, NaN) = NaN
func Hypot(p, q float64) float64
func hypot(p, q float64) float64 {
diff --git a/src/pkg/math/tanh.go b/src/pkg/math/tanh.go
index 7305be66c..cf0ffa192 100644
--- a/src/pkg/math/tanh.go
+++ b/src/pkg/math/tanh.go
@@ -65,7 +65,7 @@ var tanhQ = [...]float64{
4.84406305325125486048E3,
}
-// Tanh computes the hyperbolic tangent of x.
+// Tanh returns the hyperbolic tangent of x.
//
// Special cases are:
// Tanh(±0) = ±0
diff --git a/src/pkg/net/dial.go b/src/pkg/net/dial.go
index da5f7e302..b18d28362 100644
--- a/src/pkg/net/dial.go
+++ b/src/pkg/net/dial.go
@@ -9,112 +9,48 @@ import (
"time"
)
-// A DialOption modifies a DialOpt call.
-type DialOption interface {
- setDialOpt(*dialOpts)
-}
-
-var noLocalAddr Addr // nil
-
-// dialOpts holds all the dial options, populated by a DialOption's
-// setDialOpt.
+// A Dialer contains options for connecting to an address.
//
-// All fields may be their zero value.
-type dialOpts struct {
- deadline time.Time
- localAddr Addr
- network string // if empty, "tcp"
- deferredConnect bool
-}
-
-func (o *dialOpts) net() string {
- if o.network == "" {
- return "tcp"
+// The zero value for each field is equivalent to dialing
+// without that option. Dialing with the zero value of Dialer
+// is therefore equivalent to just calling the Dial function.
+type Dialer struct {
+ // Timeout is the maximum amount of time a dial will wait for
+ // a connect to complete. If Deadline is also set, it may fail
+ // earlier.
+ //
+ // The default is no timeout.
+ //
+ // With or without a timeout, the operating system may impose
+ // its own earlier timeout. For instance, TCP timeouts are
+ // often around 3 minutes.
+ Timeout time.Duration
+
+ // Deadline is the absolute point in time after which dials
+ // will fail. If Timeout is set, it may fail earlier.
+ // Zero means no deadline, or dependent on the operating system
+ // as with the Timeout option.
+ Deadline time.Time
+
+ // LocalAddr is the local address to use when dialing an
+ // address. The address must be of a compatible type for the
+ // network being dialed.
+ // If nil, a local address is automatically chosen.
+ LocalAddr Addr
+}
+
+// Return either now+Timeout or Deadline, whichever comes first.
+// Or zero, if neither is set.
+func (d *Dialer) deadline() time.Time {
+ if d.Timeout == 0 {
+ return d.Deadline
+ }
+ timeoutDeadline := time.Now().Add(d.Timeout)
+ if d.Deadline.IsZero() || timeoutDeadline.Before(d.Deadline) {
+ return timeoutDeadline
+ } else {
+ return d.Deadline
}
- return o.network
-}
-
-var (
- // TCP is a dial option to dial with TCP (over IPv4 or IPv6).
- TCP = Network("tcp")
-
- // UDP is a dial option to dial with UDP (over IPv4 or IPv6).
- UDP = Network("udp")
-)
-
-// Network returns a DialOption to dial using the given network.
-//
-// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
-// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
-// (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and
-// "unixpacket".
-//
-// For IP networks, net must be "ip", "ip4" or "ip6" followed
-// by a colon and a protocol number or name, such as
-// "ipv4:1" or "ip6:ospf".
-func Network(net string) DialOption {
- return dialNetwork(net)
-}
-
-type dialNetwork string
-
-func (s dialNetwork) setDialOpt(o *dialOpts) {
- o.network = string(s)
-}
-
-// Deadline returns a DialOption to fail a dial that doesn't
-// complete before t.
-func Deadline(t time.Time) DialOption {
- return dialDeadline(t)
-}
-
-type dialDeadline time.Time
-
-func (t dialDeadline) setDialOpt(o *dialOpts) {
- o.deadline = time.Time(t)
-}
-
-// Timeout returns a DialOption to fail a dial that doesn't
-// complete within the provided duration.
-func Timeout(d time.Duration) DialOption {
- return dialTimeoutOpt(d)
-}
-
-type dialTimeoutOpt time.Duration
-
-func (d dialTimeoutOpt) setDialOpt(o *dialOpts) {
- o.deadline = time.Now().Add(time.Duration(d))
-}
-
-type tcpFastOpen struct{}
-
-func (tcpFastOpen) setDialOpt(o *dialOpts) {
- o.deferredConnect = true
-}
-
-// TODO(bradfitz): implement this (golang.org/issue/4842) and unexport this.
-//
-// TCPFastTimeout returns an option to use TCP Fast Open (TFO) when
-// doing this dial. It is only valid for use with TCP connections.
-// Data sent over a TFO connection may be processed by the peer
-// multiple times, so should be used with caution.
-func todo_TCPFastTimeout() DialOption {
- return tcpFastOpen{}
-}
-
-type localAddrOption struct {
- la Addr
-}
-
-func (a localAddrOption) setDialOpt(o *dialOpts) {
- o.localAddr = a.la
-}
-
-// LocalAddress returns a dial option to perform a dial with the
-// provided local address. The address must be of a compatible type
-// for the network being dialed.
-func LocalAddress(addr Addr) DialOption {
- return localAddrOption{addr}
}
func parseNetwork(net string) (afnet string, proto int, err error) {
@@ -161,7 +97,7 @@ func resolveAddr(op, net, addr string, deadline time.Time) (Addr, error) {
return resolveInternetAddr(afnet, addr, deadline)
}
-// Dial connects to the address addr on the network net.
+// Dial connects to the address on the named network.
//
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
@@ -181,32 +117,33 @@ func resolveAddr(op, net, addr string, deadline time.Time) (Addr, error) {
// Dial("tcp", "[2001:db8::1]:http")
// Dial("tcp", "[fe80::1%lo0]:80")
//
-// For IP networks, the net must be "ip", "ip4" or "ip6" followed by a
-// colon and a protocol number or name and the addr must be a literal
-// IP address.
+// For IP networks, the network must be "ip", "ip4" or "ip6" followed
+// by a colon and a protocol number or name and the addr must be a
+// literal IP address.
//
// Examples:
// Dial("ip4:1", "127.0.0.1")
// Dial("ip6:ospf", "::1")
//
-// For Unix networks, the addr must be a file system path.
-func Dial(net, addr string) (Conn, error) {
- return DialOpt(addr, dialNetwork(net))
+// For Unix networks, the address must be a file system path.
+func Dial(network, address string) (Conn, error) {
+ var d Dialer
+ return d.Dial(network, address)
}
-// DialOpt dials addr using the provided options.
-// If no options are provided, DialOpt(addr) is equivalent
-// to Dial("tcp", addr). See Dial for the syntax of addr.
-func DialOpt(addr string, opts ...DialOption) (Conn, error) {
- var o dialOpts
- for _, opt := range opts {
- opt.setDialOpt(&o)
- }
- ra, err := resolveAddr("dial", o.net(), addr, o.deadline)
- if err != nil {
- return nil, err
- }
- return dial(o.net(), addr, o.localAddr, ra, o.deadline)
+// DialTimeout acts like Dial but takes a timeout.
+// The timeout includes name resolution, if required.
+func DialTimeout(network, address string, timeout time.Duration) (Conn, error) {
+ d := Dialer{Timeout: timeout}
+ return d.Dial(network, address)
+}
+
+// Dial connects to the address on the named network.
+//
+// See func Dial for a description of the network and address
+// parameters.
+func (d *Dialer) Dial(network, address string) (Conn, error) {
+ return resolveAndDial(network, address, d.LocalAddr, d.deadline())
}
func dial(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err error) {
@@ -235,58 +172,6 @@ func dial(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err error)
return
}
-// DialTimeout acts like Dial but takes a timeout.
-// The timeout includes name resolution, if required.
-func DialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
- return dialTimeout(net, addr, timeout)
-}
-
-// dialTimeoutRace is the old implementation of DialTimeout, still used
-// on operating systems where the deadline hasn't been pushed down
-// into the pollserver.
-// TODO: fix this on plan9.
-func dialTimeoutRace(net, addr string, timeout time.Duration) (Conn, error) {
- t := time.NewTimer(timeout)
- defer t.Stop()
- type pair struct {
- Conn
- error
- }
- ch := make(chan pair, 1)
- resolvedAddr := make(chan Addr, 1)
- go func() {
- ra, err := resolveAddr("dial", net, addr, noDeadline)
- if err != nil {
- ch <- pair{nil, err}
- return
- }
- resolvedAddr <- ra // in case we need it for OpError
- c, err := dial(net, addr, noLocalAddr, ra, noDeadline)
- ch <- pair{c, err}
- }()
- select {
- case <-t.C:
- // Try to use the real Addr in our OpError, if we resolved it
- // before the timeout. Otherwise we just use stringAddr.
- var ra Addr
- select {
- case a := <-resolvedAddr:
- ra = a
- default:
- ra = &stringAddr{net, addr}
- }
- err := &OpError{
- Op: "dial",
- Net: net,
- Addr: ra,
- Err: &timeoutError{},
- }
- return nil, err
- case p := <-ch:
- return p.Conn, p.error
- }
-}
-
type stringAddr struct {
net, addr string
}
diff --git a/src/pkg/net/dial_gen.go b/src/pkg/net/dial_gen.go
new file mode 100644
index 000000000..0a3277de4
--- /dev/null
+++ b/src/pkg/net/dial_gen.go
@@ -0,0 +1,61 @@
+// Copyright 2012 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 windows plan9
+
+package net
+
+import (
+ "time"
+)
+
+// resolveAndDialChannel is the simple pure-Go implementation of
+// resolveAndDial, still used on operating systems where the deadline
+// hasn't been pushed down into the pollserver. (Plan 9 and some old
+// versions of Windows)
+func resolveAndDialChannel(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) {
+ timeout := deadline.Sub(time.Now())
+ if timeout < 0 {
+ timeout = 0
+ }
+ t := time.NewTimer(timeout)
+ defer t.Stop()
+ type pair struct {
+ Conn
+ error
+ }
+ ch := make(chan pair, 1)
+ resolvedAddr := make(chan Addr, 1)
+ go func() {
+ ra, err := resolveAddr("dial", net, addr, noDeadline)
+ if err != nil {
+ ch <- pair{nil, err}
+ return
+ }
+ resolvedAddr <- ra // in case we need it for OpError
+ c, err := dial(net, addr, localAddr, ra, noDeadline)
+ ch <- pair{c, err}
+ }()
+ select {
+ case <-t.C:
+ // Try to use the real Addr in our OpError, if we resolved it
+ // before the timeout. Otherwise we just use stringAddr.
+ var ra Addr
+ select {
+ case a := <-resolvedAddr:
+ ra = a
+ default:
+ ra = &stringAddr{net, addr}
+ }
+ err := &OpError{
+ Op: "dial",
+ Net: net,
+ Addr: ra,
+ Err: &timeoutError{},
+ }
+ return nil, err
+ case p := <-ch:
+ return p.Conn, p.error
+ }
+}
diff --git a/src/pkg/net/example_test.go b/src/pkg/net/example_test.go
index eefe84fa7..6f2f9074c 100644
--- a/src/pkg/net/example_test.go
+++ b/src/pkg/net/example_test.go
@@ -16,6 +16,7 @@ func ExampleListener() {
if err != nil {
log.Fatal(err)
}
+ defer l.Close()
for {
// Wait for a connection.
conn, err := l.Accept()
diff --git a/src/pkg/net/fd_plan9.go b/src/pkg/net/fd_plan9.go
index 169087999..e9527a374 100644
--- a/src/pkg/net/fd_plan9.go
+++ b/src/pkg/net/fd_plan9.go
@@ -23,10 +23,10 @@ var canCancelIO = true // used for testing current package
func sysInit() {
}
-func dialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
+func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) {
// On plan9, use the relatively inefficient
// goroutine-racing implementation.
- return dialTimeoutRace(net, addr, timeout)
+ return resolveAndDialChannel(net, addr, localAddr, deadline)
}
func newFD(proto, name string, ctl, data *os.File, laddr, raddr Addr) *netFD {
diff --git a/src/pkg/net/fd_unix.go b/src/pkg/net/fd_unix.go
index 2b418a868..f52c98a6c 100644
--- a/src/pkg/net/fd_unix.go
+++ b/src/pkg/net/fd_unix.go
@@ -41,13 +41,12 @@ type netFD struct {
pd pollDesc
}
-func dialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
- deadline := time.Now().Add(timeout)
+func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) {
ra, err := resolveAddr("dial", net, addr, deadline)
if err != nil {
return nil, err
}
- return dial(net, addr, noLocalAddr, ra, deadline)
+ return dial(net, addr, localAddr, ra, deadline)
}
func newFD(fd, family, sotype int, net string) (*netFD, error) {
diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go
index 0e331b44d..3a16d8ae7 100644
--- a/src/pkg/net/fd_windows.go
+++ b/src/pkg/net/fd_windows.go
@@ -54,18 +54,17 @@ func canUseConnectEx(net string) bool {
return syscall.LoadConnectEx() == nil
}
-func dialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
+func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) {
if !canUseConnectEx(net) {
// Use the relatively inefficient goroutine-racing
// implementation of DialTimeout.
- return dialTimeoutRace(net, addr, timeout)
+ return resolveAndDialChannel(net, addr, localAddr, deadline)
}
- deadline := time.Now().Add(timeout)
ra, err := resolveAddr("dial", net, addr, deadline)
if err != nil {
return nil, err
}
- return dial(net, addr, noLocalAddr, ra, deadline)
+ return dial(net, addr, localAddr, ra, deadline)
}
// Interface for all IO operations.
@@ -138,12 +137,18 @@ type resultSrv struct {
iocp syscall.Handle
}
+func runtime_blockingSyscallHint()
+
func (s *resultSrv) Run() {
var o *syscall.Overlapped
var key uint32
var r ioResult
for {
- r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE)
+ r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, 0)
+ if r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil {
+ runtime_blockingSyscallHint()
+ r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE)
+ }
switch {
case r.err == nil:
// Dequeued successfully completed IO packet.
diff --git a/src/pkg/net/file_windows.go b/src/pkg/net/file_windows.go
index c50c32e21..ca2b9b226 100644
--- a/src/pkg/net/file_windows.go
+++ b/src/pkg/net/file_windows.go
@@ -9,16 +9,28 @@ import (
"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) {
// TODO: Implement this
return nil, os.NewSyscallError("FileConn", syscall.EWINDOWS)
}
+// 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) {
// TODO: Implement this
return nil, os.NewSyscallError("FileListener", syscall.EWINDOWS)
}
+// 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) {
// TODO: Implement this
return nil, os.NewSyscallError("FilePacketConn", syscall.EWINDOWS)
diff --git a/src/pkg/net/http/example_test.go b/src/pkg/net/http/example_test.go
index eed3beeea..bc60df7f2 100644
--- a/src/pkg/net/http/example_test.go
+++ b/src/pkg/net/http/example_test.go
@@ -51,11 +51,20 @@ func ExampleGet() {
}
func ExampleFileServer() {
- // we use StripPrefix so that /tmpfiles/somefile will access /tmp/somefile
+ // Simple static webserver:
+ log.Fatal(http.ListenAndServe(":8080", http.FileServer(http.Dir("/usr/share/doc"))))
+}
+
+func ExampleFileServer_stripPrefix() {
+ // To serve a directory on disk (/tmp) under an alternate URL
+ // path (/tmpfiles/), use StripPrefix to modify the request
+ // URL's path before the FileServer sees it:
http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp"))))
}
func ExampleStripPrefix() {
- // we use StripPrefix so that /tmpfiles/somefile will access /tmp/somefile
+ // To serve a directory on disk (/tmp) under an alternate URL
+ // path (/tmpfiles/), use StripPrefix to modify the request
+ // URL's path before the FileServer sees it:
http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp"))))
}
diff --git a/src/pkg/net/http/request.go b/src/pkg/net/http/request.go
index dabb169d1..c1f862aad 100644
--- a/src/pkg/net/http/request.go
+++ b/src/pkg/net/http/request.go
@@ -467,10 +467,42 @@ func (r *Request) SetBasicAuth(username, password string) {
r.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
}
+// parseRequestLine parses "GET /foo HTTP/1.1" into its three parts.
+func parseRequestLine(line string) (method, requestURI, proto string, ok bool) {
+ s1 := strings.Index(line, " ")
+ s2 := strings.Index(line[s1+1:], " ")
+ if s1 < 0 || s2 < 0 {
+ return
+ }
+ s2 += s1 + 1
+ return line[:s1], line[s1+1 : s2], line[s2+1:], true
+}
+
+// TODO(bradfitz): use a sync.Cache when available
+var textprotoReaderCache = make(chan *textproto.Reader, 4)
+
+func newTextprotoReader(br *bufio.Reader) *textproto.Reader {
+ select {
+ case r := <-textprotoReaderCache:
+ r.R = br
+ return r
+ default:
+ return textproto.NewReader(br)
+ }
+}
+
+func putTextprotoReader(r *textproto.Reader) {
+ r.R = nil
+ select {
+ case textprotoReaderCache <- r:
+ default:
+ }
+}
+
// ReadRequest reads and parses a request from b.
func ReadRequest(b *bufio.Reader) (req *Request, err error) {
- tp := textproto.NewReader(b)
+ tp := newTextprotoReader(b)
req = new(Request)
// First line: GET /index.html HTTP/1.0
@@ -479,18 +511,18 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) {
return nil, err
}
defer func() {
+ putTextprotoReader(tp)
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
}()
- var f []string
- if f = strings.SplitN(s, " ", 3); len(f) < 3 {
+ var ok bool
+ req.Method, req.RequestURI, req.Proto, ok = parseRequestLine(s)
+ if !ok {
return nil, &badStringError{"malformed HTTP request", s}
}
- req.Method, req.RequestURI, req.Proto = f[0], f[1], f[2]
rawurl := req.RequestURI
- var ok bool
if req.ProtoMajor, req.ProtoMinor, ok = ParseHTTPVersion(req.Proto); !ok {
return nil, &badStringError{"malformed HTTP version", req.Proto}
}
diff --git a/src/pkg/net/http/response_test.go b/src/pkg/net/http/response_test.go
index 49836ce2c..02796e88b 100644
--- a/src/pkg/net/http/response_test.go
+++ b/src/pkg/net/http/response_test.go
@@ -112,8 +112,8 @@ var respTests = []respTest{
ProtoMinor: 0,
Request: dummyReq("GET"),
Header: Header{
- "Connection": {"close"}, // TODO(rsc): Delete?
- "Content-Length": {"10"}, // TODO(rsc): Delete?
+ "Connection": {"close"},
+ "Content-Length": {"10"},
},
Close: true,
ContentLength: 10,
@@ -170,7 +170,7 @@ var respTests = []respTest{
Request: dummyReq("GET"),
Header: Header{},
Close: false,
- ContentLength: -1, // TODO(rsc): Fix?
+ ContentLength: -1,
TransferEncoding: []string{"chunked"},
},
diff --git a/src/pkg/net/http/serve_test.go b/src/pkg/net/http/serve_test.go
index 5adde545f..102f48942 100644
--- a/src/pkg/net/http/serve_test.go
+++ b/src/pkg/net/http/serve_test.go
@@ -10,6 +10,7 @@ import (
"bufio"
"bytes"
"crypto/tls"
+ "errors"
"fmt"
"io"
"io/ioutil"
@@ -64,10 +65,39 @@ func (a dummyAddr) String() string {
return string(a)
}
+type noopConn struct{}
+
+func (noopConn) LocalAddr() net.Addr { return dummyAddr("local-addr") }
+func (noopConn) RemoteAddr() net.Addr { return dummyAddr("remote-addr") }
+func (noopConn) SetDeadline(t time.Time) error { return nil }
+func (noopConn) SetReadDeadline(t time.Time) error { return nil }
+func (noopConn) SetWriteDeadline(t time.Time) error { return nil }
+
+type rwTestConn struct {
+ io.Reader
+ io.Writer
+ noopConn
+
+ closeFunc func() error // called if non-nil
+ closec chan bool // else, if non-nil, send value to it on close
+}
+
+func (c *rwTestConn) Close() error {
+ if c.closeFunc != nil {
+ return c.closeFunc()
+ }
+ select {
+ case c.closec <- true:
+ default:
+ }
+ return nil
+}
+
type testConn struct {
readBuf bytes.Buffer
writeBuf bytes.Buffer
closec chan bool // if non-nil, send value to it on close
+ noopConn
}
func (c *testConn) Read(b []byte) (int, error) {
@@ -86,26 +116,6 @@ func (c *testConn) Close() error {
return nil
}
-func (c *testConn) LocalAddr() net.Addr {
- return dummyAddr("local-addr")
-}
-
-func (c *testConn) RemoteAddr() net.Addr {
- return dummyAddr("remote-addr")
-}
-
-func (c *testConn) SetDeadline(t time.Time) error {
- return nil
-}
-
-func (c *testConn) SetReadDeadline(t time.Time) error {
- return nil
-}
-
-func (c *testConn) SetWriteDeadline(t time.Time) error {
- return nil
-}
-
func TestConsumingBodyOnNextConn(t *testing.T) {
conn := new(testConn)
for i := 0; i < 2; i++ {
@@ -1447,6 +1457,198 @@ func TestOptions(t *testing.T) {
}
}
+// Tests regarding the ordering of Write, WriteHeader, Header, and
+// Flush calls. In Go 1.0, rw.WriteHeader immediately flushed the
+// (*response).header to the wire. In Go 1.1, the actual wire flush is
+// delayed, so we could maybe tack on a Content-Length and better
+// Content-Type after we see more (or all) of the output. To preserve
+// compatibility with Go 1, we need to be careful to track which
+// headers were live at the time of WriteHeader, so we write the same
+// ones, even if the handler modifies them (~erroneously) after the
+// first Write.
+func TestHeaderToWire(t *testing.T) {
+ req := []byte(strings.Replace(`GET / HTTP/1.1
+Host: golang.org
+
+`, "\n", "\r\n", -1))
+
+ tests := []struct {
+ name string
+ handler func(ResponseWriter, *Request)
+ check func(output string) error
+ }{
+ {
+ name: "write without Header",
+ handler: func(rw ResponseWriter, r *Request) {
+ rw.Write([]byte("hello world"))
+ },
+ check: func(got string) error {
+ if !strings.Contains(got, "Content-Length:") {
+ return errors.New("no content-length")
+ }
+ if !strings.Contains(got, "Content-Type: text/plain") {
+ return errors.New("no content-length")
+ }
+ return nil
+ },
+ },
+ {
+ name: "Header mutation before write",
+ handler: func(rw ResponseWriter, r *Request) {
+ h := rw.Header()
+ h.Set("Content-Type", "some/type")
+ rw.Write([]byte("hello world"))
+ h.Set("Too-Late", "bogus")
+ },
+ check: func(got string) error {
+ if !strings.Contains(got, "Content-Length:") {
+ return errors.New("no content-length")
+ }
+ if !strings.Contains(got, "Content-Type: some/type") {
+ return errors.New("wrong content-type")
+ }
+ if strings.Contains(got, "Too-Late") {
+ return errors.New("don't want too-late header")
+ }
+ return nil
+ },
+ },
+ {
+ name: "write then useless Header mutation",
+ handler: func(rw ResponseWriter, r *Request) {
+ rw.Write([]byte("hello world"))
+ rw.Header().Set("Too-Late", "Write already wrote headers")
+ },
+ check: func(got string) error {
+ if strings.Contains(got, "Too-Late") {
+ return errors.New("header appeared from after WriteHeader")
+ }
+ return nil
+ },
+ },
+ {
+ name: "flush then write",
+ handler: func(rw ResponseWriter, r *Request) {
+ rw.(Flusher).Flush()
+ rw.Write([]byte("post-flush"))
+ rw.Header().Set("Too-Late", "Write already wrote headers")
+ },
+ check: func(got string) error {
+ if !strings.Contains(got, "Transfer-Encoding: chunked") {
+ return errors.New("not chunked")
+ }
+ if strings.Contains(got, "Too-Late") {
+ return errors.New("header appeared from after WriteHeader")
+ }
+ return nil
+ },
+ },
+ {
+ name: "header then flush",
+ handler: func(rw ResponseWriter, r *Request) {
+ rw.Header().Set("Content-Type", "some/type")
+ rw.(Flusher).Flush()
+ rw.Write([]byte("post-flush"))
+ rw.Header().Set("Too-Late", "Write already wrote headers")
+ },
+ check: func(got string) error {
+ if !strings.Contains(got, "Transfer-Encoding: chunked") {
+ return errors.New("not chunked")
+ }
+ if strings.Contains(got, "Too-Late") {
+ return errors.New("header appeared from after WriteHeader")
+ }
+ if !strings.Contains(got, "Content-Type: some/type") {
+ return errors.New("wrong content-length")
+ }
+ return nil
+ },
+ },
+ {
+ name: "sniff-on-first-write content-type",
+ handler: func(rw ResponseWriter, r *Request) {
+ rw.Write([]byte("<html><head></head><body>some html</body></html>"))
+ rw.Header().Set("Content-Type", "x/wrong")
+ },
+ check: func(got string) error {
+ if !strings.Contains(got, "Content-Type: text/html") {
+ return errors.New("wrong content-length; want html")
+ }
+ return nil
+ },
+ },
+ {
+ name: "explicit content-type wins",
+ handler: func(rw ResponseWriter, r *Request) {
+ rw.Header().Set("Content-Type", "some/type")
+ rw.Write([]byte("<html><head></head><body>some html</body></html>"))
+ },
+ check: func(got string) error {
+ if !strings.Contains(got, "Content-Type: some/type") {
+ return errors.New("wrong content-length; want html")
+ }
+ return nil
+ },
+ },
+ {
+ name: "empty handler",
+ handler: func(rw ResponseWriter, r *Request) {
+ },
+ check: func(got string) error {
+ if !strings.Contains(got, "Content-Type: text/plain") {
+ return errors.New("wrong content-length; want text/plain")
+ }
+ if !strings.Contains(got, "Content-Length: 0") {
+ return errors.New("want 0 content-length")
+ }
+ return nil
+ },
+ },
+ {
+ name: "only Header, no write",
+ handler: func(rw ResponseWriter, r *Request) {
+ rw.Header().Set("Some-Header", "some-value")
+ },
+ check: func(got string) error {
+ if !strings.Contains(got, "Some-Header") {
+ return errors.New("didn't get header")
+ }
+ return nil
+ },
+ },
+ {
+ name: "WriteHeader call",
+ handler: func(rw ResponseWriter, r *Request) {
+ rw.WriteHeader(404)
+ rw.Header().Set("Too-Late", "some-value")
+ },
+ check: func(got string) error {
+ if !strings.Contains(got, "404") {
+ return errors.New("wrong status")
+ }
+ if strings.Contains(got, "Some-Header") {
+ return errors.New("shouldn't have seen Too-Late")
+ }
+ return nil
+ },
+ },
+ }
+ for _, tc := range tests {
+ var output bytes.Buffer
+ conn := &rwTestConn{
+ Reader: bytes.NewReader(req),
+ Writer: &output,
+ closec: make(chan bool, 1),
+ }
+ ln := &oneConnListener{conn: conn}
+ go Serve(ln, HandlerFunc(tc.handler))
+ <-conn.closec
+ if err := tc.check(output.String()); err != nil {
+ t.Errorf("%s: %v\nGot response:\n%s", tc.name, err, output.Bytes())
+ }
+ }
+}
+
// goTimeout runs f, failing t if f takes more than ns to complete.
func goTimeout(t *testing.T, d time.Duration, f func()) {
ch := make(chan bool, 2)
@@ -1635,7 +1837,9 @@ Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
res := []byte("Hello world!\n")
conn := &testConn{
- closec: make(chan bool),
+ // testConn.Close will not push into the channel
+ // if it's full.
+ closec: make(chan bool, 1),
}
handler := HandlerFunc(func(rw ResponseWriter, r *Request) {
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
@@ -1651,3 +1855,146 @@ Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
<-conn.closec
}
}
+
+// repeatReader reads content count times, then EOFs.
+type repeatReader struct {
+ content []byte
+ count int
+ off int
+}
+
+func (r *repeatReader) Read(p []byte) (n int, err error) {
+ if r.count <= 0 {
+ return 0, io.EOF
+ }
+ n = copy(p, r.content[r.off:])
+ r.off += n
+ if r.off == len(r.content) {
+ r.count--
+ r.off = 0
+ }
+ return
+}
+
+func BenchmarkServerFakeConnWithKeepAlive(b *testing.B) {
+ b.ReportAllocs()
+
+ req := []byte(strings.Replace(`GET / HTTP/1.1
+Host: golang.org
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17
+Accept-Encoding: gzip,deflate,sdch
+Accept-Language: en-US,en;q=0.8
+Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
+
+`, "\n", "\r\n", -1))
+ res := []byte("Hello world!\n")
+
+ conn := &rwTestConn{
+ Reader: &repeatReader{content: req, count: b.N},
+ Writer: ioutil.Discard,
+ closec: make(chan bool, 1),
+ }
+ handled := 0
+ handler := HandlerFunc(func(rw ResponseWriter, r *Request) {
+ handled++
+ rw.Header().Set("Content-Type", "text/html; charset=utf-8")
+ rw.Write(res)
+ })
+ ln := &oneConnListener{conn: conn}
+ go Serve(ln, handler)
+ <-conn.closec
+ if b.N != handled {
+ b.Errorf("b.N=%d but handled %d", b.N, handled)
+ }
+}
+
+// same as above, but representing the most simple possible request
+// and handler. Notably: the handler does not call rw.Header().
+func BenchmarkServerFakeConnWithKeepAliveLite(b *testing.B) {
+ b.ReportAllocs()
+
+ req := []byte(strings.Replace(`GET / HTTP/1.1
+Host: golang.org
+
+`, "\n", "\r\n", -1))
+ res := []byte("Hello world!\n")
+
+ conn := &rwTestConn{
+ Reader: &repeatReader{content: req, count: b.N},
+ Writer: ioutil.Discard,
+ closec: make(chan bool, 1),
+ }
+ handled := 0
+ handler := HandlerFunc(func(rw ResponseWriter, r *Request) {
+ handled++
+ rw.Write(res)
+ })
+ ln := &oneConnListener{conn: conn}
+ go Serve(ln, handler)
+ <-conn.closec
+ if b.N != handled {
+ b.Errorf("b.N=%d but handled %d", b.N, handled)
+ }
+}
+
+const someResponse = "<html>some response</html>"
+
+// A Reponse that's just no bigger than 2KB, the buffer-before-chunking threshold.
+var response = bytes.Repeat([]byte(someResponse), 2<<10/len(someResponse))
+
+// Both Content-Type and Content-Length set. Should be no buffering.
+func BenchmarkServerHandlerTypeLen(b *testing.B) {
+ benchmarkHandler(b, HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header().Set("Content-Type", "text/html")
+ w.Header().Set("Content-Length", strconv.Itoa(len(response)))
+ w.Write(response)
+ }))
+}
+
+// A Content-Type is set, but no length. No sniffing, but will count the Content-Length.
+func BenchmarkServerHandlerNoLen(b *testing.B) {
+ benchmarkHandler(b, HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header().Set("Content-Type", "text/html")
+ w.Write(response)
+ }))
+}
+
+// A Content-Length is set, but the Content-Type will be sniffed.
+func BenchmarkServerHandlerNoType(b *testing.B) {
+ benchmarkHandler(b, HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header().Set("Content-Length", strconv.Itoa(len(response)))
+ w.Write(response)
+ }))
+}
+
+// Neither a Content-Type or Content-Length, so sniffed and counted.
+func BenchmarkServerHandlerNoHeader(b *testing.B) {
+ benchmarkHandler(b, HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Write(response)
+ }))
+}
+
+func benchmarkHandler(b *testing.B, h Handler) {
+ b.ReportAllocs()
+ req := []byte(strings.Replace(`GET / HTTP/1.1
+Host: golang.org
+
+`, "\n", "\r\n", -1))
+ conn := &rwTestConn{
+ Reader: &repeatReader{content: req, count: b.N},
+ Writer: ioutil.Discard,
+ closec: make(chan bool, 1),
+ }
+ handled := 0
+ handler := HandlerFunc(func(rw ResponseWriter, r *Request) {
+ handled++
+ h.ServeHTTP(rw, r)
+ })
+ ln := &oneConnListener{conn: conn}
+ go Serve(ln, handler)
+ <-conn.closec
+ if b.N != handled {
+ b.Errorf("b.N=%d but handled %d", b.N, handled)
+ }
+}
diff --git a/src/pkg/net/http/server.go b/src/pkg/net/http/server.go
index aee3229d3..4792bfba2 100644
--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -4,9 +4,6 @@
// HTTP server. See RFC 2616.
-// TODO(rsc):
-// logging
-
package http
import (
@@ -222,15 +219,28 @@ const bufferBeforeChunkingSize = 2048
//
// See the comment above (*response).Write for the entire write flow.
type chunkWriter struct {
- res *response
- header Header // a deep copy of r.Header, once WriteHeader is called
- wroteHeader bool // whether the header's been sent
+ res *response
+
+ // header is either nil or a deep clone of res.handlerHeader
+ // at the time of res.WriteHeader, if res.WriteHeader is
+ // called and extra buffering is being done to calculate
+ // Content-Type and/or Content-Length.
+ header Header
+
+ // wroteHeader tells whether the header's been written to "the
+ // wire" (or rather: w.conn.buf). this is unlike
+ // (*response).wroteHeader, which tells only whether it was
+ // logically written.
+ wroteHeader bool
// set by the writeHeader method:
chunking bool // using chunked transfer encoding for reply body
}
-var crlf = []byte("\r\n")
+var (
+ crlf = []byte("\r\n")
+ colonSpace = []byte(": ")
+)
func (cw *chunkWriter) Write(p []byte) (n int, err error) {
if !cw.wroteHeader {
@@ -280,13 +290,15 @@ type response struct {
wroteContinue bool // 100 Continue response was written
w *bufio.Writer // buffers output in chunks to chunkWriter
- cw *chunkWriter
+ cw chunkWriter
+ sw *switchWriter // of the bufio.Writer, for return to putBufioWriter
// handlerHeader is the Header that Handlers get access to,
// which may be retained and mutated even after WriteHeader.
// handlerHeader is copied into cw.header at WriteHeader
// time, and privately mutated thereafter.
handlerHeader Header
+ calledHeader bool // handler accessed handlerHeader via Header
written int64 // number of bytes written in body
contentLength int64 // explicitly-declared Content-Length; or -1
@@ -381,7 +393,7 @@ func (srv *Server) newConn(rwc net.Conn) (c *conn, err error) {
c.sr = liveSwitchReader{r: c.rwc}
c.lr = io.LimitReader(&c.sr, noLimit).(*io.LimitedReader)
br, sr := newBufioReader(c.lr)
- bw, sw := newBufioWriter(c.rwc)
+ bw, sw := newBufioWriterSize(c.rwc, 4<<10)
c.buf = bufio.NewReadWriter(br, bw)
c.bufswr = sr
c.bufsww = sw
@@ -402,10 +414,21 @@ type bufioWriterPair struct {
// TODO: use a sync.Cache instead
var (
- bufioReaderCache = make(chan bufioReaderPair, 4)
- bufioWriterCache = make(chan bufioWriterPair, 4)
+ bufioReaderCache = make(chan bufioReaderPair, 4)
+ bufioWriterCache2k = make(chan bufioWriterPair, 4)
+ bufioWriterCache4k = make(chan bufioWriterPair, 4)
)
+func bufioWriterCache(size int) chan bufioWriterPair {
+ switch size {
+ case 2 << 10:
+ return bufioWriterCache2k
+ case 4 << 10:
+ return bufioWriterCache4k
+ }
+ return nil
+}
+
func newBufioReader(r io.Reader) (*bufio.Reader, *switchReader) {
select {
case p := <-bufioReaderCache:
@@ -429,14 +452,14 @@ func putBufioReader(br *bufio.Reader, sr *switchReader) {
}
}
-func newBufioWriter(w io.Writer) (*bufio.Writer, *switchWriter) {
+func newBufioWriterSize(w io.Writer, size int) (*bufio.Writer, *switchWriter) {
select {
- case p := <-bufioWriterCache:
+ case p := <-bufioWriterCache(size):
p.sw.Writer = w
return p.bw, p.sw
default:
sw := &switchWriter{w}
- return bufio.NewWriter(sw), sw
+ return bufio.NewWriterSize(sw, size), sw
}
}
@@ -454,7 +477,7 @@ func putBufioWriter(bw *bufio.Writer, sw *switchWriter) {
}
sw.Writer = nil
select {
- case bufioWriterCache <- bufioWriterPair{bw, sw}:
+ case bufioWriterCache(bw.Available()) <- bufioWriterPair{bw, sw}:
default:
}
}
@@ -537,14 +560,20 @@ func (c *conn) readRequest() (w *response, err error) {
req: req,
handlerHeader: make(Header),
contentLength: -1,
- cw: new(chunkWriter),
}
w.cw.res = w
- w.w = bufio.NewWriterSize(w.cw, bufferBeforeChunkingSize)
+ w.w, w.sw = newBufioWriterSize(&w.cw, bufferBeforeChunkingSize)
return w, nil
}
func (w *response) Header() Header {
+ if w.cw.header == nil && w.wroteHeader && !w.cw.wroteHeader {
+ // Accessing the header between logically writing it
+ // and physically writing it means we need to allocate
+ // a clone to snapshot the logically written state.
+ w.cw.header = w.handlerHeader.clone()
+ }
+ w.calledHeader = true
return w.handlerHeader
}
@@ -571,15 +600,48 @@ func (w *response) WriteHeader(code int) {
w.wroteHeader = true
w.status = code
- w.cw.header = w.handlerHeader.clone()
+ if w.calledHeader && w.cw.header == nil {
+ w.cw.header = w.handlerHeader.clone()
+ }
- if cl := w.cw.header.get("Content-Length"); cl != "" {
+ if cl := w.handlerHeader.get("Content-Length"); cl != "" {
v, err := strconv.ParseInt(cl, 10, 64)
if err == nil && v >= 0 {
w.contentLength = v
} else {
log.Printf("http: invalid Content-Length of %q", cl)
- w.cw.header.Del("Content-Length")
+ w.handlerHeader.Del("Content-Length")
+ }
+ }
+}
+
+// extraHeader is the set of headers sometimes added by chunkWriter.writeHeader.
+// This type is used to avoid extra allocations from cloning and/or populating
+// the response Header map and all its 1-element slices.
+type extraHeader struct {
+ contentType string
+ contentLength string
+ connection string
+ date string
+ transferEncoding string
+}
+
+// Sorted the same as extraHeader.Write's loop.
+var extraHeaderKeys = [][]byte{
+ []byte("Content-Type"), []byte("Content-Length"),
+ []byte("Connection"), []byte("Date"), []byte("Transfer-Encoding"),
+}
+
+// The value receiver, despite copying 5 strings to the stack,
+// prevents an extra allocation. The escape analysis isn't smart
+// enough to realize this doesn't mutate h.
+func (h extraHeader) Write(w io.Writer) {
+ for i, v := range []string{h.contentType, h.contentLength, h.connection, h.date, h.transferEncoding} {
+ if v != "" {
+ w.Write(extraHeaderKeys[i])
+ w.Write(colonSpace)
+ io.WriteString(w, v)
+ w.Write(crlf)
}
}
}
@@ -599,23 +661,47 @@ func (cw *chunkWriter) writeHeader(p []byte) {
cw.wroteHeader = true
w := cw.res
- code := w.status
- done := w.handlerDone
+
+ // header is written out to w.conn.buf below. Depending on the
+ // state of the handler, we either own the map or not. If we
+ // don't own it, the exclude map is created lazily for
+ // WriteSubset to remove headers. The setHeader struct holds
+ // headers we need to add.
+ header := cw.header
+ owned := header != nil
+ if !owned {
+ header = w.handlerHeader
+ }
+ var excludeHeader map[string]bool
+ delHeader := func(key string) {
+ if owned {
+ header.Del(key)
+ return
+ }
+ if _, ok := header[key]; !ok {
+ return
+ }
+ if excludeHeader == nil {
+ excludeHeader = make(map[string]bool)
+ }
+ excludeHeader[key] = true
+ }
+ var setHeader extraHeader
// If the handler is done but never sent a Content-Length
// response header and this is our first (and last) write, set
// it, even to zero. This helps HTTP/1.0 clients keep their
// "keep-alive" connections alive.
- if done && cw.header.get("Content-Length") == "" && w.req.Method != "HEAD" {
+ if w.handlerDone && header.get("Content-Length") == "" && w.req.Method != "HEAD" {
w.contentLength = int64(len(p))
- cw.header.Set("Content-Length", strconv.Itoa(len(p)))
+ setHeader.contentLength = strconv.Itoa(len(p))
}
// If this was an HTTP/1.0 request with keep-alive and we sent a
// Content-Length back, we can make this a keep-alive response ...
if w.req.wantsHttp10KeepAlive() {
- sentLength := cw.header.get("Content-Length") != ""
- if sentLength && cw.header.get("Connection") == "keep-alive" {
+ sentLength := header.get("Content-Length") != ""
+ if sentLength && header.get("Connection") == "keep-alive" {
w.closeAfterReply = false
}
}
@@ -624,15 +710,15 @@ func (cw *chunkWriter) writeHeader(p []byte) {
hasCL := w.contentLength != -1
if w.req.wantsHttp10KeepAlive() && (w.req.Method == "HEAD" || hasCL) {
- _, connectionHeaderSet := cw.header["Connection"]
+ _, connectionHeaderSet := header["Connection"]
if !connectionHeaderSet {
- cw.header.Set("Connection", "keep-alive")
+ setHeader.connection = "keep-alive"
}
} else if !w.req.ProtoAtLeast(1, 1) || w.req.wantsClose() {
w.closeAfterReply = true
}
- if cw.header.get("Connection") == "close" {
+ if header.get("Connection") == "close" {
w.closeAfterReply = true
}
@@ -646,49 +732,49 @@ func (cw *chunkWriter) writeHeader(p []byte) {
n, _ := io.CopyN(ioutil.Discard, w.req.Body, maxPostHandlerReadBytes+1)
if n >= maxPostHandlerReadBytes {
w.requestTooLarge()
- cw.header.Set("Connection", "close")
+ delHeader("Connection")
+ setHeader.connection = "close"
} else {
w.req.Body.Close()
}
}
}
+ code := w.status
if code == StatusNotModified {
// Must not have body.
- for _, header := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} {
- // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers"
- if cw.header.get(header) != "" {
- cw.header.Del(header)
- }
+ // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers"
+ for _, k := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} {
+ delHeader(k)
}
} else {
// If no content type, apply sniffing algorithm to body.
- if cw.header.get("Content-Type") == "" && w.req.Method != "HEAD" {
- cw.header.Set("Content-Type", DetectContentType(p))
+ if header.get("Content-Type") == "" && w.req.Method != "HEAD" {
+ setHeader.contentType = DetectContentType(p)
}
}
- if _, ok := cw.header["Date"]; !ok {
- cw.header.Set("Date", time.Now().UTC().Format(TimeFormat))
+ if _, ok := header["Date"]; !ok {
+ setHeader.date = time.Now().UTC().Format(TimeFormat)
}
- te := cw.header.get("Transfer-Encoding")
+ te := header.get("Transfer-Encoding")
hasTE := te != ""
if hasCL && hasTE && te != "identity" {
// TODO: return an error if WriteHeader gets a return parameter
// For now just ignore the Content-Length.
log.Printf("http: WriteHeader called with both Transfer-Encoding of %q and a Content-Length of %d",
te, w.contentLength)
- cw.header.Del("Content-Length")
+ delHeader("Content-Length")
hasCL = false
}
if w.req.Method == "HEAD" || code == StatusNotModified {
// do nothing
} else if code == StatusNoContent {
- cw.header.Del("Transfer-Encoding")
+ delHeader("Transfer-Encoding")
} else if hasCL {
- cw.header.Del("Transfer-Encoding")
+ 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.
@@ -696,29 +782,63 @@ func (cw *chunkWriter) writeHeader(p []byte) {
// might have set. Deal with that as need arises once we have a valid
// use case.
cw.chunking = true
- cw.header.Set("Transfer-Encoding", "chunked")
+ setHeader.transferEncoding = "chunked"
} else {
// HTTP version < 1.1: cannot do chunked transfer
// encoding and we don't know the Content-Length so
// signal EOF by closing connection.
w.closeAfterReply = true
- cw.header.Del("Transfer-Encoding") // in case already set
+ delHeader("Transfer-Encoding") // in case already set
}
// Cannot use Content-Length with non-identity Transfer-Encoding.
if cw.chunking {
- cw.header.Del("Content-Length")
+ delHeader("Content-Length")
}
if !w.req.ProtoAtLeast(1, 0) {
return
}
if w.closeAfterReply && !hasToken(cw.header.get("Connection"), "close") {
- cw.header.Set("Connection", "close")
+ delHeader("Connection")
+ setHeader.connection = "close"
}
+ io.WriteString(w.conn.buf, statusLine(w.req, code))
+ cw.header.WriteSubset(w.conn.buf, excludeHeader)
+ setHeader.Write(w.conn.buf)
+ w.conn.buf.Write(crlf)
+}
+
+// statusLines is a cache of Status-Line strings, keyed by code (for
+// HTTP/1.1) or negative code (for HTTP/1.0). This is faster than a
+// map keyed by struct of two fields. This map's max size is bounded
+// by 2*len(statusText), two protocol types for each known official
+// status code in the statusText map.
+var (
+ statusMu sync.RWMutex
+ statusLines = make(map[int]string)
+)
+
+// statusLine returns a response Status-Line (RFC 2616 Section 6.1)
+// for the given request and response status code.
+func statusLine(req *Request, code int) string {
+ // Fast path:
+ key := code
+ proto11 := req.ProtoAtLeast(1, 1)
+ if !proto11 {
+ key = -key
+ }
+ statusMu.RLock()
+ line, ok := statusLines[key]
+ statusMu.RUnlock()
+ if ok {
+ return line
+ }
+
+ // Slow path:
proto := "HTTP/1.0"
- if w.req.ProtoAtLeast(1, 1) {
+ if proto11 {
proto = "HTTP/1.1"
}
codestring := strconv.Itoa(code)
@@ -726,9 +846,13 @@ func (cw *chunkWriter) writeHeader(p []byte) {
if !ok {
text = "status code " + codestring
}
- io.WriteString(w.conn.buf, proto+" "+codestring+" "+text+"\r\n")
- cw.header.Write(w.conn.buf)
- w.conn.buf.Write(crlf)
+ line = proto + " " + codestring + " " + text + "\r\n"
+ if ok {
+ statusMu.Lock()
+ defer statusMu.Unlock()
+ statusLines[key] = line
+ }
+ return line
}
// bodyAllowed returns true if a Write is allowed for this response type.
@@ -802,6 +926,7 @@ func (w *response) finishRequest() {
}
w.w.Flush()
+ putBufioWriter(w.w, w.sw)
w.cw.close()
w.conn.buf.Flush()
diff --git a/src/pkg/net/http/transfer.go b/src/pkg/net/http/transfer.go
index 43c6023a3..53569bcc2 100644
--- a/src/pkg/net/http/transfer.go
+++ b/src/pkg/net/http/transfer.go
@@ -328,12 +328,13 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
switch {
case chunked(t.TransferEncoding):
if noBodyExpected(t.RequestMethod) {
- t.Body = &body{Reader: io.LimitReader(r, 0), closing: t.Close}
+ t.Body = &body{Reader: eofReader, closing: t.Close}
} else {
t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close}
}
- case realLength >= 0:
- // TODO: limit the Content-Length. This is an easy DoS vector.
+ case realLength == 0:
+ t.Body = &body{Reader: eofReader, closing: t.Close}
+ case realLength > 0:
t.Body = &body{Reader: io.LimitReader(r, realLength), closing: t.Close}
default:
// realLength < 0, i.e. "Content-Length" not mentioned in header
@@ -342,7 +343,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
t.Body = &body{Reader: r, closing: t.Close}
} else {
// Persistent connection (i.e. HTTP/1.1)
- t.Body = &body{Reader: io.LimitReader(r, 0), closing: t.Close}
+ t.Body = &body{Reader: eofReader, closing: t.Close}
}
}
@@ -612,30 +613,26 @@ func (b *body) Close() error {
if b.closed {
return nil
}
- defer func() {
- b.closed = true
- }()
- if b.hdr == nil && b.closing {
+ var err error
+ switch {
+ case b.hdr == nil && b.closing:
// no trailer and closing the connection next.
// no point in reading to EOF.
- return nil
- }
-
- // In a server request, don't continue reading from the client
- // if we've already hit the maximum body size set by the
- // handler. If this is set, that also means the TCP connection
- // is about to be closed, so getting to the next HTTP request
- // in the stream is not necessary.
- if b.res != nil && b.res.requestBodyLimitHit {
- return nil
- }
-
- // Fully consume the body, which will also lead to us reading
- // the trailer headers after the body, if present.
- if _, err := io.Copy(ioutil.Discard, b); err != nil {
- return err
+ case b.res != nil && b.res.requestBodyLimitHit:
+ // In a server request, don't continue reading from the client
+ // if we've already hit the maximum body size set by the
+ // handler. If this is set, that also means the TCP connection
+ // is about to be closed, so getting to the next HTTP request
+ // in the stream is not necessary.
+ case b.Reader == eofReader:
+ // Nothing to read. No need to io.Copy from it.
+ default:
+ // Fully consume the body, which will also lead to us reading
+ // the trailer headers after the body, if present.
+ _, err = io.Copy(ioutil.Discard, b)
}
- return nil
+ b.closed = true
+ return err
}
// parseContentLength trims whitespace from s and returns -1 if no value
diff --git a/src/pkg/net/http/transport.go b/src/pkg/net/http/transport.go
index 08ced2c3d..4cd0533ff 100644
--- a/src/pkg/net/http/transport.go
+++ b/src/pkg/net/http/transport.go
@@ -58,7 +58,7 @@ type Transport struct {
// Dial specifies the dial function for creating TCP
// connections.
// If Dial is nil, net.Dial is used.
- Dial func(net, addr string) (c net.Conn, err error)
+ Dial func(network, addr string) (net.Conn, error)
// TLSClientConfig specifies the TLS configuration to use with
// tls.Client. If nil, the default configuration is used.
@@ -686,6 +686,14 @@ func (pc *persistConn) readLoop() {
var resp *Response
if err == nil {
resp, err = ReadResponse(pc.br, rc.req)
+ if err == nil && resp.StatusCode == 100 {
+ // Skip any 100-continue for now.
+ // TODO(bradfitz): if rc.req had "Expect: 100-continue",
+ // actually block the request body write and signal the
+ // writeLoop now to begin sending it. (Issue 2184) For now we
+ // eat it, since we're never expecting one.
+ resp, err = ReadResponse(pc.br, rc.req)
+ }
}
hasBody := resp != nil && rc.req.Method != "HEAD" && resp.ContentLength != 0
@@ -707,7 +715,10 @@ func (pc *persistConn) readLoop() {
resp.Body = &bodyEOFSignal{body: resp.Body}
}
- if err != nil || resp.Close || rc.req.Close {
+ if err != nil || resp.Close || rc.req.Close || resp.StatusCode <= 199 {
+ // Don't do keep-alive on error if either party requested a close
+ // or we get an unexpected informational (1xx) response.
+ // StatusCode 100 is already handled above.
alive = false
}
diff --git a/src/pkg/net/http/transport_test.go b/src/pkg/net/http/transport_test.go
index 4bb711b17..9f64a6e4b 100644
--- a/src/pkg/net/http/transport_test.go
+++ b/src/pkg/net/http/transport_test.go
@@ -7,6 +7,7 @@
package http_test
import (
+ "bufio"
"bytes"
"compress/gzip"
"crypto/rand"
@@ -950,14 +951,28 @@ func TestTransportConcurrency(t *testing.T) {
fmt.Fprintf(w, "%v", r.FormValue("echo"))
}))
defer ts.Close()
- tr := &Transport{}
+
+ var wg sync.WaitGroup
+ wg.Add(numReqs)
+
+ tr := &Transport{
+ Dial: func(netw, addr string) (c net.Conn, err error) {
+ // Due to the Transport's "socket late
+ // binding" (see idleConnCh in transport.go),
+ // the numReqs HTTP requests below can finish
+ // with a dial still outstanding. So count
+ // our dials as work too so the leak checker
+ // doesn't complain at us.
+ wg.Add(1)
+ defer wg.Done()
+ return net.Dial(netw, addr)
+ },
+ }
defer tr.CloseIdleConnections()
c := &Client{Transport: tr}
reqs := make(chan string)
defer close(reqs)
- var wg sync.WaitGroup
- wg.Add(numReqs)
for i := 0; i < maxProcs*2; i++ {
go func() {
for req := range reqs {
@@ -1385,6 +1400,114 @@ func TestTransportSocketLateBinding(t *testing.T) {
dialGate <- true
}
+// Issue 2184
+func TestTransportReading100Continue(t *testing.T) {
+ defer afterTest(t)
+
+ const numReqs = 5
+ reqBody := func(n int) string { return fmt.Sprintf("request body %d", n) }
+ reqID := func(n int) string { return fmt.Sprintf("REQ-ID-%d", n) }
+
+ send100Response := func(w *io.PipeWriter, r *io.PipeReader) {
+ defer w.Close()
+ defer r.Close()
+ br := bufio.NewReader(r)
+ n := 0
+ for {
+ n++
+ req, err := ReadRequest(br)
+ if err == io.EOF {
+ return
+ }
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ slurp, err := ioutil.ReadAll(req.Body)
+ if err != nil {
+ t.Errorf("Server request body slurp: %v", err)
+ return
+ }
+ id := req.Header.Get("Request-Id")
+ resCode := req.Header.Get("X-Want-Response-Code")
+ if resCode == "" {
+ resCode = "100 Continue"
+ if string(slurp) != reqBody(n) {
+ t.Errorf("Server got %q, %v; want %q", slurp, err, reqBody(n))
+ }
+ }
+ body := fmt.Sprintf("Response number %d", n)
+ v := []byte(strings.Replace(fmt.Sprintf(`HTTP/1.1 %s
+Date: Thu, 28 Feb 2013 17:55:41 GMT
+
+HTTP/1.1 200 OK
+Content-Type: text/html
+Echo-Request-Id: %s
+Content-Length: %d
+
+%s`, resCode, id, len(body), body), "\n", "\r\n", -1))
+ w.Write(v)
+ if id == reqID(numReqs) {
+ return
+ }
+ }
+
+ }
+
+ tr := &Transport{
+ Dial: func(n, addr string) (net.Conn, error) {
+ sr, sw := io.Pipe() // server read/write
+ cr, cw := io.Pipe() // client read/write
+ conn := &rwTestConn{
+ Reader: cr,
+ Writer: sw,
+ closeFunc: func() error {
+ sw.Close()
+ cw.Close()
+ return nil
+ },
+ }
+ go send100Response(cw, sr)
+ return conn, nil
+ },
+ DisableKeepAlives: false,
+ }
+ defer tr.CloseIdleConnections()
+ c := &Client{Transport: tr}
+
+ testResponse := func(req *Request, name string, wantCode int) {
+ res, err := c.Do(req)
+ if err != nil {
+ t.Fatalf("%s: Do: %v", name, err)
+ }
+ if res.StatusCode != wantCode {
+ t.Fatalf("%s: Response Statuscode=%d; want %d", name, res.StatusCode, wantCode)
+ }
+ if id, idBack := req.Header.Get("Request-Id"), res.Header.Get("Echo-Request-Id"); id != "" && id != idBack {
+ t.Errorf("%s: response id %q != request id %q", name, idBack, id)
+ }
+ _, err = ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatalf("%s: Slurp error: %v", name, err)
+ }
+ }
+
+ // Few 100 responses, making sure we're not off-by-one.
+ for i := 1; i <= numReqs; i++ {
+ req, _ := NewRequest("POST", "http://dummy.tld/", strings.NewReader(reqBody(i)))
+ req.Header.Set("Request-Id", reqID(i))
+ testResponse(req, fmt.Sprintf("100, %d/%d", i, numReqs), 200)
+ }
+
+ // And some other informational 1xx but non-100 responses, to test
+ // we return them but don't re-use the connection.
+ for i := 1; i <= numReqs; i++ {
+ req, _ := NewRequest("POST", "http://other.tld/", strings.NewReader(reqBody(i)))
+ req.Header.Set("X-Want-Response-Code", "123 Sesame Street")
+ testResponse(req, fmt.Sprintf("123, %d/%d", i, numReqs), 123)
+ }
+}
+
type proxyFromEnvTest struct {
req string // URL to fetch; blank means "http://example.com"
env string
diff --git a/src/pkg/net/http/z_last_test.go b/src/pkg/net/http/z_last_test.go
index a80fb01d0..bf5e3f188 100644
--- a/src/pkg/net/http/z_last_test.go
+++ b/src/pkg/net/http/z_last_test.go
@@ -27,7 +27,11 @@ func interestingGoroutines() (gs []string) {
strings.Contains(stack, "created by net.startServer") ||
strings.Contains(stack, "created by testing.RunTests") ||
strings.Contains(stack, "closeWriteAndWait") ||
- strings.Contains(stack, "testing.Main(") {
+ 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)
@@ -65,8 +69,6 @@ func afterTest(t *testing.T) {
if testing.Short() {
return
}
- buf := make([]byte, 1<<20)
- var stacks string
var bad string
badSubstring := map[string]string{
").readLoop(": "a Transport",
@@ -75,9 +77,10 @@ func afterTest(t *testing.T) {
"timeoutHandler": "a TimeoutHandler",
"net.(*netFD).connect(": "a timing out dial",
}
+ var stacks string
for i := 0; i < 4; i++ {
bad = ""
- stacks = string(buf[:runtime.Stack(buf, true)])
+ stacks = strings.Join(interestingGoroutines(), "\n\n")
for substr, what := range badSubstring {
if strings.Contains(stacks, substr) {
bad = what
@@ -90,6 +93,5 @@ func afterTest(t *testing.T) {
// shutting down, so give it some time.
time.Sleep(250 * time.Millisecond)
}
- gs := interestingGoroutines()
- t.Errorf("Test appears to have leaked %s:\n%s", bad, strings.Join(gs, "\n\n"))
+ t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks)
}
diff --git a/src/pkg/net/interface_unix_test.go b/src/pkg/net/interface_unix_test.go
index 6dbd6e6e7..0a453c095 100644
--- a/src/pkg/net/interface_unix_test.go
+++ b/src/pkg/net/interface_unix_test.go
@@ -41,8 +41,11 @@ func (ti *testInterface) teardown() error {
}
func TestPointToPointInterface(t *testing.T) {
- switch runtime.GOOS {
- case "darwin":
+ if testing.Short() {
+ t.Skip("skipping test in short mode")
+ }
+ switch {
+ case runtime.GOOS == "darwin":
t.Skipf("skipping read test on %q", runtime.GOOS)
}
if os.Getuid() != 0 {
@@ -90,6 +93,9 @@ func TestPointToPointInterface(t *testing.T) {
}
func TestInterfaceArrivalAndDeparture(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in short mode")
+ }
if os.Getuid() != 0 {
t.Skip("skipping test; must be root")
}
diff --git a/src/pkg/net/ipraw_test.go b/src/pkg/net/ipraw_test.go
index 841b57ab4..6cdb5ab84 100644
--- a/src/pkg/net/ipraw_test.go
+++ b/src/pkg/net/ipraw_test.go
@@ -14,12 +14,14 @@ import (
"time"
)
-var resolveIPAddrTests = []struct {
+type resolveIPAddrTest struct {
net string
litAddr string
addr *IPAddr
err error
-}{
+}
+
+var resolveIPAddrTests = []resolveIPAddrTest{
{"ip", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
{"ip4", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
{"ip4:icmp", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
@@ -30,8 +32,6 @@ var resolveIPAddrTests = []struct {
{"ip", "::1%en0", &IPAddr{IP: ParseIP("::1"), Zone: "en0"}, nil},
{"ip6", "::1%911", &IPAddr{IP: ParseIP("::1"), Zone: "911"}, nil},
- {"ip6", "fe80::1", &IPAddr{IP: ParseIP("fe80::1"), Zone: "name"}, nil},
- {"ip6", "fe80::1", &IPAddr{IP: ParseIP("fe80::1"), Zone: "index"}, nil},
{"", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, // Go 1.0 behavior
{"", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, // Go 1.0 behavior
@@ -41,23 +41,18 @@ var resolveIPAddrTests = []struct {
{"tcp", "1.2.3.4:123", nil, UnknownNetworkError("tcp")},
}
+func init() {
+ if ifi := loopbackInterface(); ifi != nil {
+ index := fmt.Sprintf("%v", ifi.Index)
+ resolveIPAddrTests = append(resolveIPAddrTests, []resolveIPAddrTest{
+ {"ip6", "fe80::1%" + ifi.Name, &IPAddr{IP: ParseIP("fe80::1"), Zone: zoneToString(ifi.Index)}, nil},
+ {"ip6", "fe80::1%" + index, &IPAddr{IP: ParseIP("fe80::1"), Zone: index}, nil},
+ }...)
+ }
+}
+
func TestResolveIPAddr(t *testing.T) {
for _, tt := range resolveIPAddrTests {
- if tt.addr != nil && (tt.addr.Zone == "name" || tt.addr.Zone == "index") {
- ifi := loopbackInterface()
- if ifi == nil {
- continue
- }
- switch tt.addr.Zone {
- case "name":
- tt.litAddr += "%" + ifi.Name
- tt.addr.Zone = zoneToString(ifi.Index)
- case "index":
- index := fmt.Sprintf("%v", ifi.Index)
- tt.litAddr += "%" + index
- tt.addr.Zone = index
- }
- }
addr, err := ResolveIPAddr(tt.net, tt.litAddr)
if err != tt.err {
t.Fatalf("ResolveIPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err)
diff --git a/src/pkg/net/iprawsock.go b/src/pkg/net/iprawsock.go
index 9d99e7591..0be94eb70 100644
--- a/src/pkg/net/iprawsock.go
+++ b/src/pkg/net/iprawsock.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Raw IP sockets
-
package net
// IPAddr represents the address of an IP end point.
diff --git a/src/pkg/net/iprawsock_plan9.go b/src/pkg/net/iprawsock_plan9.go
index 43a5eab41..e62d116b8 100644
--- a/src/pkg/net/iprawsock_plan9.go
+++ b/src/pkg/net/iprawsock_plan9.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Raw IP sockets for Plan 9
-
package net
import (
@@ -76,7 +74,7 @@ func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn,
}
// ListenIP listens for incoming IP packets addressed to the local
-// address laddr. The returned connection c's ReadFrom and WriteTo
+// address laddr. The returned connection's ReadFrom and WriteTo
// methods can be used to receive and send IP packets with per-packet
// addressing.
func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
diff --git a/src/pkg/net/iprawsock_posix.go b/src/pkg/net/iprawsock_posix.go
index 1bcaef9f2..caeeb4653 100644
--- a/src/pkg/net/iprawsock_posix.go
+++ b/src/pkg/net/iprawsock_posix.go
@@ -4,8 +4,6 @@
// +build darwin freebsd linux netbsd openbsd windows
-// Raw IP sockets for POSIX
-
package net
import (
@@ -51,8 +49,8 @@ func (a *IPAddr) toAddr() sockaddr {
return a
}
-// IPConn is the implementation of the Conn and PacketConn
-// interfaces for IP network connections.
+// IPConn is the implementation of the Conn and PacketConn interfaces
+// for IP network connections.
type IPConn struct {
conn
}
@@ -116,12 +114,13 @@ func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err
return
}
-// WriteToIP writes an IP packet to addr via c, copying the payload from b.
+// WriteToIP writes an IP packet to addr via c, copying the payload
+// from b.
//
-// WriteToIP can be made to time out and return
-// an error with Timeout() == true after a fixed time limit;
-// see SetDeadline and SetWriteDeadline.
-// On packet-oriented connections, write timeouts are rare.
+// WriteToIP can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetWriteDeadline. On packet-oriented connections, write timeouts
+// are rare.
func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) {
if !c.ok() {
return 0, syscall.EINVAL
@@ -159,8 +158,9 @@ func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error
return c.fd.WriteMsg(b, oob, sa)
}
-// DialIP connects to the remote address raddr on the network protocol netProto,
-// which must be "ip", "ip4", or "ip6" followed by a colon and a protocol number or name.
+// DialIP connects to the remote address raddr on the network protocol
+// netProto, which must be "ip", "ip4", or "ip6" followed by a colon
+// and a protocol number or name.
func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
return dialIP(netProto, laddr, raddr, noDeadline)
}
@@ -185,10 +185,10 @@ func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn,
return newIPConn(fd), nil
}
-// ListenIP listens for incoming IP packets addressed to the
-// local address laddr. The returned connection c's ReadFrom
-// and WriteTo methods can be used to receive and send IP
-// packets with per-packet addressing.
+// ListenIP listens for incoming IP packets addressed to the local
+// address laddr. The returned connection's ReadFrom and WriteTo
+// methods can be used to receive and send IP packets with per-packet
+// addressing.
func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
net, proto, err := parseNetwork(netProto)
if err != nil {
diff --git a/src/pkg/net/multicast_test.go b/src/pkg/net/multicast_test.go
index 1eb6a420d..8ff02a3c9 100644
--- a/src/pkg/net/multicast_test.go
+++ b/src/pkg/net/multicast_test.go
@@ -93,7 +93,7 @@ var ipv6MulticastListenerTests = []struct {
// port.
func TestIPv6MulticastListener(t *testing.T) {
switch runtime.GOOS {
- case "plan9", "solaris", "windows":
+ case "plan9", "solaris":
t.Skipf("skipping test on %q", runtime.GOOS)
}
if !supportsIPv6 {
diff --git a/src/pkg/net/server_test.go b/src/pkg/net/server_test.go
index c101ffecd..9194a8ec2 100644
--- a/src/pkg/net/server_test.go
+++ b/src/pkg/net/server_test.go
@@ -402,7 +402,7 @@ func runDatagramConnClient(t *testing.T, net, laddr, taddr string, isEmpty bool)
t.Fatalf("Dial(%q, %q) failed: %v", net, taddr, err)
}
case "unixgram":
- c, err = DialUnix(net, &UnixAddr{laddr, net}, &UnixAddr{taddr, net})
+ c, err = DialUnix(net, &UnixAddr{Name: laddr, Net: net}, &UnixAddr{Name: taddr, Net: net})
if err != nil {
t.Fatalf("DialUnix(%q, {%q, %q}) failed: %v", net, laddr, taddr, err)
}
diff --git a/src/pkg/net/smtp/auth.go b/src/pkg/net/smtp/auth.go
index d401e3c21..3f1339ebc 100644
--- a/src/pkg/net/smtp/auth.go
+++ b/src/pkg/net/smtp/auth.go
@@ -54,7 +54,16 @@ func PlainAuth(identity, username, password, host string) Auth {
func (a *plainAuth) Start(server *ServerInfo) (string, []byte, error) {
if !server.TLS {
- return "", nil, errors.New("unencrypted connection")
+ advertised := false
+ for _, mechanism := range server.Auth {
+ if mechanism == "PLAIN" {
+ advertised = true
+ break
+ }
+ }
+ if !advertised {
+ return "", nil, errors.New("unencrypted connection")
+ }
}
if server.Name != a.host {
return "", nil, errors.New("wrong host name")
diff --git a/src/pkg/net/smtp/smtp_test.go b/src/pkg/net/smtp/smtp_test.go
index 8317428cb..c190b32c0 100644
--- a/src/pkg/net/smtp/smtp_test.go
+++ b/src/pkg/net/smtp/smtp_test.go
@@ -57,6 +57,41 @@ testLoop:
}
}
+func TestAuthPlain(t *testing.T) {
+ auth := PlainAuth("foo", "bar", "baz", "servername")
+
+ tests := []struct {
+ server *ServerInfo
+ err string
+ }{
+ {
+ server: &ServerInfo{Name: "servername", TLS: true},
+ },
+ {
+ // Okay; explicitly advertised by server.
+ server: &ServerInfo{Name: "servername", Auth: []string{"PLAIN"}},
+ },
+ {
+ server: &ServerInfo{Name: "servername", Auth: []string{"CRAM-MD5"}},
+ err: "unencrypted connection",
+ },
+ {
+ server: &ServerInfo{Name: "attacker", TLS: true},
+ err: "wrong host name",
+ },
+ }
+ for i, tt := range tests {
+ _, _, err := auth.Start(tt.server)
+ got := ""
+ if err != nil {
+ got = err.Error()
+ }
+ if got != tt.err {
+ t.Errorf("%d. got error = %q; want %q", i, got, tt.err)
+ }
+ }
+}
+
type faker struct {
io.ReadWriter
}
diff --git a/src/pkg/net/sock_posix.go b/src/pkg/net/sock_posix.go
index 2ebde8799..e2487c805 100644
--- a/src/pkg/net/sock_posix.go
+++ b/src/pkg/net/sock_posix.go
@@ -25,7 +25,8 @@ func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr,
return nil, err
}
- if ulsa != nil {
+ // This socket is used by a listener.
+ if ulsa != nil && ursa == nil {
// We provide a socket that listens to a wildcard
// address with reusable UDP port when the given ulsa
// is an appropriate UDP multicast address prefix.
@@ -37,6 +38,9 @@ func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr,
closesocket(s)
return nil, err
}
+ }
+
+ if ulsa != nil {
if err = syscall.Bind(s, ulsa); err != nil {
closesocket(s)
return nil, err
@@ -48,6 +52,7 @@ func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr,
return nil, err
}
+ // This socket is used by a dialer.
if ursa != nil {
if !deadline.IsZero() {
setWriteDeadline(fd, deadline)
@@ -65,10 +70,10 @@ func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr,
lsa, _ := syscall.Getsockname(s)
laddr := toAddr(lsa)
rsa, _ := syscall.Getpeername(s)
+ if rsa == nil {
+ rsa = ursa
+ }
raddr := toAddr(rsa)
fd.setAddr(laddr, raddr)
- if fd.raddr == nil {
- fd.raddr = toAddr(ursa)
- }
return fd, nil
}
diff --git a/src/pkg/net/tcp_test.go b/src/pkg/net/tcp_test.go
index add8e4823..a71b02b47 100644
--- a/src/pkg/net/tcp_test.go
+++ b/src/pkg/net/tcp_test.go
@@ -147,12 +147,14 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
}
}
-var resolveTCPAddrTests = []struct {
+type resolveTCPAddrTest struct {
net string
litAddr string
addr *TCPAddr
err error
-}{
+}
+
+var resolveTCPAddrTests = []resolveTCPAddrTest{
{"tcp", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil},
{"tcp4", "127.0.0.1:65535", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil},
@@ -161,8 +163,6 @@ var resolveTCPAddrTests = []struct {
{"tcp", "[::1%en0]:1", &TCPAddr{IP: ParseIP("::1"), Port: 1, Zone: "en0"}, nil},
{"tcp6", "[::1%911]:2", &TCPAddr{IP: ParseIP("::1"), Port: 2, Zone: "911"}, nil},
- {"tcp6", "[fe80::1]:3", &TCPAddr{IP: ParseIP("fe80::1"), Port: 3, Zone: "name"}, nil},
- {"tcp6", "[fe80::1]:4", &TCPAddr{IP: ParseIP("fe80::1"), Port: 4, Zone: "index"}, nil},
{"", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior
{"", "[::1]:0", &TCPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior
@@ -170,26 +170,18 @@ var resolveTCPAddrTests = []struct {
{"http", "127.0.0.1:0", nil, UnknownNetworkError("http")},
}
+func init() {
+ if ifi := loopbackInterface(); ifi != nil {
+ index := fmt.Sprintf("%v", ifi.Index)
+ resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
+ {"tcp6", "[fe80::1%" + ifi.Name + "]:3", &TCPAddr{IP: ParseIP("fe80::1"), Port: 3, Zone: zoneToString(ifi.Index)}, nil},
+ {"tcp6", "[fe80::1%" + index + "]:4", &TCPAddr{IP: ParseIP("fe80::1"), Port: 4, Zone: index}, nil},
+ }...)
+ }
+}
+
func TestResolveTCPAddr(t *testing.T) {
for _, tt := range resolveTCPAddrTests {
- if tt.addr != nil && (tt.addr.Zone == "name" || tt.addr.Zone == "index") {
- ifi := loopbackInterface()
- if ifi == nil {
- continue
- }
- i := last(tt.litAddr, ']')
- if i > 0 {
- switch tt.addr.Zone {
- case "name":
- tt.litAddr = tt.litAddr[:i] + "%" + ifi.Name + tt.litAddr[i:]
- tt.addr.Zone = zoneToString(ifi.Index)
- case "index":
- index := fmt.Sprintf("%v", ifi.Index)
- tt.litAddr = tt.litAddr[:i] + "%" + index + tt.litAddr[i:]
- tt.addr.Zone = index
- }
- }
- }
addr, err := ResolveTCPAddr(tt.net, tt.litAddr)
if err != tt.err {
t.Fatalf("ResolveTCPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err)
@@ -217,14 +209,12 @@ func TestTCPListenerName(t *testing.T) {
for _, tt := range tcpListenerNameTests {
ln, err := ListenTCP(tt.net, tt.laddr)
if err != nil {
- t.Errorf("ListenTCP failed: %v", err)
- return
+ t.Fatalf("ListenTCP failed: %v", err)
}
defer ln.Close()
la := ln.Addr()
if a, ok := la.(*TCPAddr); !ok || a.Port == 0 {
- t.Errorf("got %v; expected a proper address with non-zero port number", la)
- return
+ t.Fatalf("got %v; expected a proper address with non-zero port number", la)
}
}
}
diff --git a/src/pkg/net/tcpsock.go b/src/pkg/net/tcpsock.go
index 27db11568..4d9ebd214 100644
--- a/src/pkg/net/tcpsock.go
+++ b/src/pkg/net/tcpsock.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// TCP sockets
-
package net
// TCPAddr represents the address of a TCP end point.
diff --git a/src/pkg/net/tcpsock_plan9.go b/src/pkg/net/tcpsock_plan9.go
index ed3664603..96cbcc246 100644
--- a/src/pkg/net/tcpsock_plan9.go
+++ b/src/pkg/net/tcpsock_plan9.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// TCP sockets for Plan 9
-
package net
import (
@@ -165,8 +163,8 @@ func (l *TCPListener) File() (f *os.File, err error) { return l.dup() }
// ListenTCP announces on the TCP address laddr and returns a TCP
// listener. Net must be "tcp", "tcp4", or "tcp6". If laddr has a
-// port of 0, it means to listen on some available port. The caller
-// can use l.Addr() to retrieve the chosen address.
+// port of 0, ListenTCP will choose an available port. The caller can
+// use the Addr method of TCPListener to retrieve the chosen address.
func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
switch net {
case "tcp", "tcp4", "tcp6":
diff --git a/src/pkg/net/tcpsock_posix.go b/src/pkg/net/tcpsock_posix.go
index bd5a2a287..8133ca1c1 100644
--- a/src/pkg/net/tcpsock_posix.go
+++ b/src/pkg/net/tcpsock_posix.go
@@ -4,8 +4,6 @@
// +build darwin freebsd linux netbsd openbsd windows
-// TCP sockets
-
package net
import (
@@ -58,8 +56,8 @@ func (a *TCPAddr) toAddr() sockaddr {
return a
}
-// TCPConn is an implementation of the Conn interface
-// for TCP network connections.
+// TCPConn is an implementation of the Conn interface for TCP network
+// connections.
type TCPConn struct {
conn
}
@@ -96,17 +94,17 @@ func (c *TCPConn) CloseWrite() error {
return c.fd.CloseWrite()
}
-// SetLinger sets the behavior of Close() on a connection
-// which still has data waiting to be sent or to be acknowledged.
+// SetLinger sets the behavior of Close() on a connection which still
+// has data waiting to be sent or to be acknowledged.
//
-// If sec < 0 (the default), Close returns immediately and
-// the operating system finishes sending the data in the background.
+// If sec < 0 (the default), Close returns immediately and the
+// operating system finishes sending the data in the background.
//
// If sec == 0, Close returns immediately and the operating system
// discards any unsent or unacknowledged data.
//
-// If sec > 0, Close blocks for at most sec seconds waiting for
-// data to be sent and acknowledged.
+// If sec > 0, Close blocks for at most sec seconds waiting for data
+// to be sent and acknowledged.
func (c *TCPConn) SetLinger(sec int) error {
if !c.ok() {
return syscall.EINVAL
@@ -124,9 +122,9 @@ func (c *TCPConn) SetKeepAlive(keepalive bool) error {
}
// SetNoDelay controls whether the operating system should delay
-// packet transmission in hopes of sending fewer packets
-// (Nagle's algorithm). The default is true (no delay), meaning
-// that data is sent as soon as possible after a Write.
+// packet transmission in hopes of sending fewer packets (Nagle's
+// algorithm). The default is true (no delay), meaning that data is
+// sent as soon as possible after a Write.
func (c *TCPConn) SetNoDelay(noDelay bool) error {
if !c.ok() {
return syscall.EINVAL
@@ -135,8 +133,8 @@ func (c *TCPConn) SetNoDelay(noDelay bool) error {
}
// DialTCP connects to the remote address raddr on the network net,
-// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
-// as the local address for the connection.
+// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is
+// used as the local address for the connection.
func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
switch net {
case "tcp", "tcp4", "tcp6":
@@ -216,16 +214,15 @@ func spuriousENOTAVAIL(err error) bool {
return ok && e.Err == syscall.EADDRNOTAVAIL
}
-// TCPListener is a TCP network listener.
-// Clients should typically use variables of type Listener
-// instead of assuming TCP.
+// TCPListener is a TCP network listener. Clients should typically
+// use variables of type Listener instead of assuming TCP.
type TCPListener struct {
fd *netFD
}
-// AcceptTCP accepts the next incoming call and returns the new connection
-// and the remote address.
-func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) {
+// AcceptTCP accepts the next incoming call and returns the new
+// connection and the remote address.
+func (l *TCPListener) AcceptTCP() (*TCPConn, error) {
if l == nil || l.fd == nil {
return nil, syscall.EINVAL
}
@@ -236,14 +233,14 @@ func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) {
return newTCPConn(fd), nil
}
-// Accept implements the Accept method in the Listener interface;
-// it waits for the next call and returns a generic Conn.
-func (l *TCPListener) Accept() (c Conn, err error) {
- c1, err := l.AcceptTCP()
+// Accept implements the Accept method in the Listener interface; it
+// waits for the next call and returns a generic Conn.
+func (l *TCPListener) Accept() (Conn, error) {
+ c, err := l.AcceptTCP()
if err != nil {
return nil, err
}
- return c1, nil
+ return c, nil
}
// Close stops listening on the TCP address.
@@ -267,15 +264,15 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
return setDeadline(l.fd, t)
}
-// File returns a copy of the underlying os.File, set to blocking mode.
-// It is the caller's responsibility to close f when finished.
+// File returns a copy of the underlying os.File, set to blocking
+// mode. It is the caller's responsibility to close f when finished.
// Closing l does not affect f, and closing f does not affect l.
func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() }
-// ListenTCP announces on the TCP address laddr and returns a TCP listener.
-// Net must be "tcp", "tcp4", or "tcp6".
-// If laddr has a port of 0, it means to listen on some available port.
-// The caller can use l.Addr() to retrieve the chosen address.
+// ListenTCP announces on the TCP address laddr and returns a TCP
+// listener. Net must be "tcp", "tcp4", or "tcp6". If laddr has a
+// port of 0, ListenTCP will choose an available port. The caller can
+// use the Addr method of TCPListener to retrieve the chosen address.
func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
switch net {
case "tcp", "tcp4", "tcp6":
diff --git a/src/pkg/net/textproto/reader.go b/src/pkg/net/textproto/reader.go
index 35e27acb5..5bd26ac8d 100644
--- a/src/pkg/net/textproto/reader.go
+++ b/src/pkg/net/textproto/reader.go
@@ -574,6 +574,7 @@ var commonHeaders = []string{
"Content-Length",
"Content-Transfer-Encoding",
"Content-Type",
+ "Cookie",
"Date",
"Dkim-Signature",
"Etag",
diff --git a/src/pkg/net/textproto/reader_test.go b/src/pkg/net/textproto/reader_test.go
index 26987f611..f27042d4e 100644
--- a/src/pkg/net/textproto/reader_test.go
+++ b/src/pkg/net/textproto/reader_test.go
@@ -290,6 +290,7 @@ Non-Interned: test
`, "\n", "\r\n", -1)
func BenchmarkReadMIMEHeader(b *testing.B) {
+ b.ReportAllocs()
var buf bytes.Buffer
br := bufio.NewReader(&buf)
r := NewReader(br)
@@ -319,6 +320,7 @@ func BenchmarkReadMIMEHeader(b *testing.B) {
}
func BenchmarkUncommon(b *testing.B) {
+ b.ReportAllocs()
var buf bytes.Buffer
br := bufio.NewReader(&buf)
r := NewReader(br)
diff --git a/src/pkg/net/udp_test.go b/src/pkg/net/udp_test.go
index b3cafb096..4278f6dd4 100644
--- a/src/pkg/net/udp_test.go
+++ b/src/pkg/net/udp_test.go
@@ -11,12 +11,14 @@ import (
"testing"
)
-var resolveUDPAddrTests = []struct {
+type resolveUDPAddrTest struct {
net string
litAddr string
addr *UDPAddr
err error
-}{
+}
+
+var resolveUDPAddrTests = []resolveUDPAddrTest{
{"udp", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil},
{"udp4", "127.0.0.1:65535", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil},
@@ -25,8 +27,6 @@ var resolveUDPAddrTests = []struct {
{"udp", "[::1%en0]:1", &UDPAddr{IP: ParseIP("::1"), Port: 1, Zone: "en0"}, nil},
{"udp6", "[::1%911]:2", &UDPAddr{IP: ParseIP("::1"), Port: 2, Zone: "911"}, nil},
- {"udp6", "[fe80::1]:3", &UDPAddr{IP: ParseIP("fe80::1"), Port: 3, Zone: "name"}, nil},
- {"udp6", "[fe80::1]:4", &UDPAddr{IP: ParseIP("fe80::1"), Port: 4, Zone: "index"}, nil},
{"", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior
{"", "[::1]:0", &UDPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior
@@ -34,26 +34,18 @@ var resolveUDPAddrTests = []struct {
{"sip", "127.0.0.1:0", nil, UnknownNetworkError("sip")},
}
+func init() {
+ if ifi := loopbackInterface(); ifi != nil {
+ index := fmt.Sprintf("%v", ifi.Index)
+ resolveUDPAddrTests = append(resolveUDPAddrTests, []resolveUDPAddrTest{
+ {"udp6", "[fe80::1%" + ifi.Name + "]:3", &UDPAddr{IP: ParseIP("fe80::1"), Port: 3, Zone: zoneToString(ifi.Index)}, nil},
+ {"udp6", "[fe80::1%" + index + "]:4", &UDPAddr{IP: ParseIP("fe80::1"), Port: 4, Zone: index}, nil},
+ }...)
+ }
+}
+
func TestResolveUDPAddr(t *testing.T) {
for _, tt := range resolveUDPAddrTests {
- if tt.addr != nil && (tt.addr.Zone == "name" || tt.addr.Zone == "index") {
- ifi := loopbackInterface()
- if ifi == nil {
- continue
- }
- i := last(tt.litAddr, ']')
- if i > 0 {
- switch tt.addr.Zone {
- case "name":
- tt.litAddr = tt.litAddr[:i] + "%" + ifi.Name + tt.litAddr[i:]
- tt.addr.Zone = zoneToString(ifi.Index)
- case "index":
- index := fmt.Sprintf("%v", ifi.Index)
- tt.litAddr = tt.litAddr[:i] + "%" + index + tt.litAddr[i:]
- tt.addr.Zone = index
- }
- }
- }
addr, err := ResolveUDPAddr(tt.net, tt.litAddr)
if err != tt.err {
t.Fatalf("ResolveUDPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err)
@@ -159,14 +151,50 @@ func TestUDPConnLocalName(t *testing.T) {
for _, tt := range udpConnLocalNameTests {
c, err := ListenUDP(tt.net, tt.laddr)
if err != nil {
- t.Errorf("ListenUDP failed: %v", err)
- return
+ t.Fatalf("ListenUDP failed: %v", err)
}
defer c.Close()
la := c.LocalAddr()
if a, ok := la.(*UDPAddr); !ok || a.Port == 0 {
- t.Errorf("got %v; expected a proper address with non-zero port number", la)
- return
+ t.Fatalf("got %v; expected a proper address with non-zero port number", la)
+ }
+ }
+}
+
+func TestUDPConnLocalAndRemoteNames(t *testing.T) {
+ for _, laddr := range []string{"", "127.0.0.1:0"} {
+ c1, err := ListenPacket("udp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("ListenUDP failed: %v", err)
+ }
+ defer c1.Close()
+
+ var la *UDPAddr
+ if laddr != "" {
+ var err error
+ if la, err = ResolveUDPAddr("udp", laddr); err != nil {
+ t.Fatalf("ResolveUDPAddr failed: %v", err)
+ }
+ }
+ c2, err := DialUDP("udp", la, c1.LocalAddr().(*UDPAddr))
+ if err != nil {
+ t.Fatalf("DialUDP failed: %v", err)
+ }
+ defer c2.Close()
+
+ var connAddrs = [4]struct {
+ got Addr
+ ok bool
+ }{
+ {c1.LocalAddr(), true},
+ {c1.(*UDPConn).RemoteAddr(), false},
+ {c2.LocalAddr(), true},
+ {c2.RemoteAddr(), true},
+ }
+ for _, ca := range connAddrs {
+ if a, ok := ca.got.(*UDPAddr); ok != ca.ok || ok && a.Port == 0 {
+ t.Fatalf("got %v; expected a proper address with non-zero port number", ca.got)
+ }
}
}
}
diff --git a/src/pkg/net/udpsock.go b/src/pkg/net/udpsock.go
index 277050606..5ce7d6bea 100644
--- a/src/pkg/net/udpsock.go
+++ b/src/pkg/net/udpsock.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// UDP sockets
-
package net
import "errors"
diff --git a/src/pkg/net/udpsock_plan9.go b/src/pkg/net/udpsock_plan9.go
index 66dcbab7c..12a348399 100644
--- a/src/pkg/net/udpsock_plan9.go
+++ b/src/pkg/net/udpsock_plan9.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// UDP sockets for Plan 9
-
package net
import (
@@ -13,15 +11,13 @@ import (
"time"
)
-// UDPConn is the implementation of the Conn and PacketConn
-// interfaces for UDP network connections.
+// UDPConn is the implementation of the Conn and PacketConn interfaces
+// for UDP network connections.
type UDPConn struct {
conn
}
-func newUDPConn(fd *netFD) *UDPConn {
- return &UDPConn{conn{fd}}
-}
+func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} }
// ReadFromUDP reads a UDP packet from c, copying the payload into b.
// It returns the number of bytes copied into b and the return address
@@ -164,7 +160,10 @@ func unmarshalUDPHeader(b []byte) (*udpHeader, []byte) {
}
// ListenUDP listens for incoming UDP packets addressed to the local
-// address laddr. The returned connection c's ReadFrom and WriteTo
+// address laddr. Net must be "udp", "udp4", or "udp6". If laddr has
+// a port of 0, ListenUDP will choose an available port.
+// The LocalAddr method of the returned UDPConn can be used to
+// discover the port. The returned connection's ReadFrom and WriteTo
// methods can be used to receive and send UDP packets with per-packet
// addressing.
func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
diff --git a/src/pkg/net/udpsock_posix.go b/src/pkg/net/udpsock_posix.go
index 9aafb7083..b90cb030d 100644
--- a/src/pkg/net/udpsock_posix.go
+++ b/src/pkg/net/udpsock_posix.go
@@ -4,8 +4,6 @@
// +build darwin freebsd linux netbsd openbsd windows
-// UDP sockets for POSIX
-
package net
import (
@@ -51,8 +49,8 @@ func (a *UDPAddr) toAddr() sockaddr {
return a
}
-// UDPConn is the implementation of the Conn and PacketConn
-// interfaces for UDP network connections.
+// UDPConn is the implementation of the Conn and PacketConn interfaces
+// for UDP network connections.
type UDPConn struct {
conn
}
@@ -63,8 +61,9 @@ func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} }
// It returns the number of bytes copied into b and the return address
// that was on the packet.
//
-// ReadFromUDP can be made to time out and return an error with Timeout() == true
-// after a fixed time limit; see SetDeadline and SetReadDeadline.
+// ReadFromUDP can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetReadDeadline.
func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
if !c.ok() {
return 0, nil, syscall.EINVAL
@@ -108,12 +107,13 @@ func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr,
return
}
-// WriteToUDP writes a UDP packet to addr via c, copying the payload from b.
+// WriteToUDP writes a UDP packet to addr via c, copying the payload
+// from b.
//
-// WriteToUDP can be made to time out and return
-// an error with Timeout() == true after a fixed time limit;
-// see SetDeadline and SetWriteDeadline.
-// On packet-oriented connections, write timeouts are rare.
+// WriteToUDP can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetWriteDeadline. On packet-oriented connections, write timeouts
+// are rare.
func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
if !c.ok() {
return 0, syscall.EINVAL
@@ -158,8 +158,8 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er
}
// DialUDP connects to the remote address raddr on the network net,
-// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used
-// as the local address for the connection.
+// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is
+// used as the local address for the connection.
func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
return dialUDP(net, laddr, raddr, noDeadline)
}
@@ -180,10 +180,13 @@ func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, e
return newUDPConn(fd), nil
}
-// ListenUDP listens for incoming UDP packets addressed to the
-// local address laddr. The returned connection c's ReadFrom
-// and WriteTo methods can be used to receive and send UDP
-// packets with per-packet addressing.
+// ListenUDP listens for incoming UDP packets addressed to the local
+// address laddr. Net must be "udp", "udp4", or "udp6". If laddr has
+// a port of 0, ListenUDP will choose an available port.
+// The LocalAddr method of the returned UDPConn can be used to
+// discover the port. The returned connection's ReadFrom and WriteTo
+// methods can be used to receive and send UDP packets with per-packet
+// addressing.
func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
switch net {
case "udp", "udp4", "udp6":
@@ -201,9 +204,9 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
}
// ListenMulticastUDP listens for incoming multicast UDP packets
-// addressed to the group address gaddr on ifi, which specifies
-// the interface to join. ListenMulticastUDP uses default
-// multicast interface if ifi is nil.
+// addressed to the group address gaddr on ifi, which specifies the
+// interface to join. ListenMulticastUDP uses default multicast
+// interface if ifi is nil.
func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
switch net {
case "udp", "udp4", "udp6":
diff --git a/src/pkg/net/unicast_posix_test.go b/src/pkg/net/unicast_posix_test.go
index a8855cab7..b0588f4e5 100644
--- a/src/pkg/net/unicast_posix_test.go
+++ b/src/pkg/net/unicast_posix_test.go
@@ -45,7 +45,7 @@ var listenerTests = []struct {
// same port.
func TestTCPListener(t *testing.T) {
switch runtime.GOOS {
- case "plan9", "windows":
+ case "plan9":
t.Skipf("skipping test on %q", runtime.GOOS)
}
@@ -69,65 +69,8 @@ func TestTCPListener(t *testing.T) {
// same port.
func TestUDPListener(t *testing.T) {
switch runtime.GOOS {
- case "plan9", "windows":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
-
- toudpnet := func(net string) string {
- switch net {
- case "tcp":
- return "udp"
- case "tcp4":
- return "udp4"
- case "tcp6":
- return "udp6"
- }
- return "<nil>"
- }
-
- for _, tt := range listenerTests {
- if tt.wildcard && (testing.Short() || !*testExternal) {
- continue
- }
- if tt.ipv6 && !supportsIPv6 {
- continue
- }
- tt.net = toudpnet(tt.net)
- l1, port := usableListenPacketPort(t, tt.net, tt.laddr)
- checkFirstListener(t, tt.net, tt.laddr+":"+port, l1)
- l2, err := ListenPacket(tt.net, tt.laddr+":"+port)
- checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2)
- l1.Close()
- }
-}
-
-func TestSimpleTCPListener(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- return
- }
-
- for _, tt := range listenerTests {
- if tt.wildcard && (testing.Short() || !*testExternal) {
- continue
- }
- if tt.ipv6 {
- continue
- }
- l1, port := usableListenPort(t, tt.net, tt.laddr)
- checkFirstListener(t, tt.net, tt.laddr+":"+port, l1)
- l2, err := Listen(tt.net, tt.laddr+":"+port)
- checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2)
- l1.Close()
- }
-}
-
-func TestSimpleUDPListener(t *testing.T) {
- switch runtime.GOOS {
case "plan9":
t.Skipf("skipping test on %q", runtime.GOOS)
- return
}
toudpnet := func(net string) string {
@@ -146,7 +89,7 @@ func TestSimpleUDPListener(t *testing.T) {
if tt.wildcard && (testing.Short() || !*testExternal) {
continue
}
- if tt.ipv6 {
+ if tt.ipv6 && !supportsIPv6 {
continue
}
tt.net = toudpnet(tt.net)
@@ -231,7 +174,7 @@ func TestDualStackTCPListener(t *testing.T) {
t.Skipf("skipping test on %q", runtime.GOOS)
}
if !supportsIPv6 {
- return
+ t.Skip("ipv6 is not supported")
}
for _, tt := range dualStackListenerTests {
@@ -263,7 +206,7 @@ func TestDualStackUDPListener(t *testing.T) {
t.Skipf("skipping test on %q", runtime.GOOS)
}
if !supportsIPv6 {
- return
+ t.Skip("ipv6 is not supported")
}
toudpnet := func(net string) string {
diff --git a/src/pkg/net/unix_test.go b/src/pkg/net/unix_test.go
index 2eaabe86e..5e63e9d9d 100644
--- a/src/pkg/net/unix_test.go
+++ b/src/pkg/net/unix_test.go
@@ -33,7 +33,6 @@ func TestReadUnixgramWithUnnamedSocket(t *testing.T) {
off := make(chan bool)
data := [5]byte{1, 2, 3, 4, 5}
-
go func() {
defer func() { off <- true }()
s, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0)
@@ -54,15 +53,13 @@ func TestReadUnixgramWithUnnamedSocket(t *testing.T) {
c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
n, from, err := c.ReadFrom(b)
if err != nil {
- t.Errorf("UnixConn.ReadFrom failed: %v", err)
- return
+ t.Fatalf("UnixConn.ReadFrom failed: %v", err)
}
if from != nil {
- t.Errorf("neighbor address is %v", from)
+ t.Fatalf("neighbor address is %v", from)
}
if !bytes.Equal(b[:n], data[:]) {
- t.Errorf("got %v, want %v", b[:n], data[:])
- return
+ t.Fatalf("got %v, want %v", b[:n], data[:])
}
}
@@ -101,13 +98,12 @@ func TestReadUnixgramWithZeroBytesBuffer(t *testing.T) {
<-off
c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
- var peer Addr
- if _, peer, err = c.ReadFrom(nil); err != nil {
- t.Errorf("UnixConn.ReadFrom failed: %v", err)
- return
+ _, from, err := c.ReadFrom(nil)
+ if err != nil {
+ t.Fatalf("UnixConn.ReadFrom failed: %v", err)
}
- if peer != nil {
- t.Errorf("peer adddress is %v", peer)
+ if from != nil {
+ t.Fatalf("neighbor address is %v", from)
}
}
@@ -126,10 +122,10 @@ func TestUnixAutobind(t *testing.T) {
// retrieve the autobind address
autoAddr := c1.LocalAddr().(*UnixAddr)
if len(autoAddr.Name) <= 1 {
- t.Fatalf("Invalid autobind address: %v", autoAddr)
+ t.Fatalf("invalid autobind address: %v", autoAddr)
}
if autoAddr.Name[0] != '@' {
- t.Fatalf("Invalid autobind address: %v", autoAddr)
+ t.Fatalf("invalid autobind address: %v", autoAddr)
}
c2, err := DialUnix("unixgram", nil, autoAddr)
@@ -139,6 +135,112 @@ func TestUnixAutobind(t *testing.T) {
defer c2.Close()
if !reflect.DeepEqual(c1.LocalAddr(), c2.RemoteAddr()) {
- t.Fatalf("Expected autobind address %v, got %v", c1.LocalAddr(), c2.RemoteAddr())
+ t.Fatalf("expected autobind address %v, got %v", c1.LocalAddr(), c2.RemoteAddr())
+ }
+}
+
+func TestUnixConnLocalAndRemoteNames(t *testing.T) {
+ for _, laddr := range []string{"", testUnixAddr()} {
+ taddr := testUnixAddr()
+ ta, err := ResolveUnixAddr("unix", taddr)
+ if err != nil {
+ t.Fatalf("ResolveUnixAddr failed: %v", err)
+ }
+ ln, err := ListenUnix("unix", ta)
+ if err != nil {
+ t.Fatalf("ListenUnix failed: %v", err)
+ }
+ defer func() {
+ ln.Close()
+ os.Remove(taddr)
+ }()
+
+ done := make(chan int)
+ go transponder(t, ln, done)
+
+ la, err := ResolveUnixAddr("unix", laddr)
+ if err != nil {
+ t.Fatalf("ResolveUnixAddr failed: %v", err)
+ }
+ c, err := DialUnix("unix", la, ta)
+ if err != nil {
+ t.Fatalf("DialUnix failed: %v", err)
+ }
+ defer func() {
+ c.Close()
+ if la != nil {
+ defer os.Remove(laddr)
+ }
+ }()
+ if _, err := c.Write([]byte("UNIXCONN LOCAL AND REMOTE NAME TEST")); err != nil {
+ t.Fatalf("UnixConn.Write failed: %v", err)
+ }
+
+ if runtime.GOOS == "linux" && laddr == "" {
+ laddr = "@" // autobind feature
+ }
+ var connAddrs = [3]struct{ got, want Addr }{
+ {ln.Addr(), ta},
+ {c.LocalAddr(), &UnixAddr{Name: laddr, Net: "unix"}},
+ {c.RemoteAddr(), ta},
+ }
+ for _, ca := range connAddrs {
+ if !reflect.DeepEqual(ca.got, ca.want) {
+ t.Fatalf("got %#v, expected %#v", ca.got, ca.want)
+ }
+ }
+
+ <-done
+ }
+}
+
+func TestUnixgramConnLocalAndRemoteNames(t *testing.T) {
+ for _, laddr := range []string{"", testUnixAddr()} {
+ taddr := testUnixAddr()
+ ta, err := ResolveUnixAddr("unixgram", taddr)
+ if err != nil {
+ t.Fatalf("ResolveUnixAddr failed: %v", err)
+ }
+ c1, err := ListenUnixgram("unixgram", ta)
+ if err != nil {
+ t.Fatalf("ListenUnixgram failed: %v", err)
+ }
+ defer func() {
+ c1.Close()
+ os.Remove(taddr)
+ }()
+
+ var la *UnixAddr
+ if laddr != "" {
+ var err error
+ if la, err = ResolveUnixAddr("unixgram", laddr); err != nil {
+ t.Fatalf("ResolveUnixAddr failed: %v", err)
+ }
+ }
+ c2, err := DialUnix("unixgram", la, ta)
+ if err != nil {
+ t.Fatalf("DialUnix failed: %v", err)
+ }
+ defer func() {
+ c2.Close()
+ if la != nil {
+ defer os.Remove(laddr)
+ }
+ }()
+
+ if runtime.GOOS == "linux" && laddr == "" {
+ laddr = "@" // autobind feature
+ }
+ var connAddrs = [4]struct{ got, want Addr }{
+ {c1.LocalAddr(), ta},
+ {c1.RemoteAddr(), nil},
+ {c2.LocalAddr(), &UnixAddr{Name: laddr, Net: "unixgram"}},
+ {c2.RemoteAddr(), ta},
+ }
+ for _, ca := range connAddrs {
+ if !reflect.DeepEqual(ca.got, ca.want) {
+ t.Fatalf("got %#v, expected %#v", ca.got, ca.want)
+ }
+ }
}
}
diff --git a/src/pkg/net/unixsock.go b/src/pkg/net/unixsock.go
index 977ff9103..21a19eca2 100644
--- a/src/pkg/net/unixsock.go
+++ b/src/pkg/net/unixsock.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Unix domain sockets
-
package net
// UnixAddr represents the address of a Unix domain socket end point.
diff --git a/src/pkg/net/unixsock_plan9.go b/src/pkg/net/unixsock_plan9.go
index 00a0be5b0..0390207f0 100644
--- a/src/pkg/net/unixsock_plan9.go
+++ b/src/pkg/net/unixsock_plan9.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Unix domain sockets stubs for Plan 9
-
package net
import (
@@ -133,9 +131,9 @@ func (l *UnixListener) File() (*os.File, error) {
}
// ListenUnixgram listens for incoming Unix datagram packets addressed
-// to the local address laddr. The returned connection c's ReadFrom
-// and WriteTo methods can be used to receive and send packets with
-// per-packet addressing. The network net must be "unixgram".
+// to the local address laddr. The network net must be "unixgram".
+// The returned connection's ReadFrom and WriteTo methods can be used
+// to receive and send packets with per-packet addressing.
func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/src/pkg/net/unixsock_posix.go b/src/pkg/net/unixsock_posix.go
index 8d57dacbd..760d38f27 100644
--- a/src/pkg/net/unixsock_posix.go
+++ b/src/pkg/net/unixsock_posix.go
@@ -4,8 +4,6 @@
// +build darwin freebsd linux netbsd openbsd windows
-// Unix domain sockets
-
package net
import (
@@ -15,6 +13,13 @@ import (
"time"
)
+func (a *UnixAddr) isUnnamed() bool {
+ if a == nil || a.Name == "" {
+ return true
+ }
+ return false
+}
+
func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.Time) (*netFD, error) {
var sotype int
switch net {
@@ -31,12 +36,12 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.T
var la, ra syscall.Sockaddr
switch mode {
case "dial":
- if laddr != nil {
+ if !laddr.isUnnamed() {
la = &syscall.SockaddrUnix{Name: laddr.Name}
}
if raddr != nil {
ra = &syscall.SockaddrUnix{Name: raddr.Name}
- } else if sotype != syscall.SOCK_DGRAM || laddr == nil {
+ } else if sotype != syscall.SOCK_DGRAM || laddr.isUnnamed() {
return nil, &OpError{Op: mode, Net: net, Err: errMissingAddress}
}
case "listen":
@@ -69,21 +74,21 @@ error:
func sockaddrToUnix(sa syscall.Sockaddr) Addr {
if s, ok := sa.(*syscall.SockaddrUnix); ok {
- return &UnixAddr{s.Name, "unix"}
+ return &UnixAddr{Name: s.Name, Net: "unix"}
}
return nil
}
func sockaddrToUnixgram(sa syscall.Sockaddr) Addr {
if s, ok := sa.(*syscall.SockaddrUnix); ok {
- return &UnixAddr{s.Name, "unixgram"}
+ return &UnixAddr{Name: s.Name, Net: "unixgram"}
}
return nil
}
func sockaddrToUnixpacket(sa syscall.Sockaddr) Addr {
if s, ok := sa.(*syscall.SockaddrUnix); ok {
- return &UnixAddr{s.Name, "unixpacket"}
+ return &UnixAddr{Name: s.Name, Net: "unixpacket"}
}
return nil
}
@@ -92,10 +97,10 @@ func sotypeToNet(sotype int) string {
switch sotype {
case syscall.SOCK_STREAM:
return "unix"
- case syscall.SOCK_SEQPACKET:
- return "unixpacket"
case syscall.SOCK_DGRAM:
return "unixgram"
+ case syscall.SOCK_SEQPACKET:
+ return "unixpacket"
default:
panic("sotypeToNet unknown socket type")
}
@@ -124,7 +129,7 @@ func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) {
switch sa := sa.(type) {
case *syscall.SockaddrUnix:
if sa.Name != "" {
- addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)}
+ addr = &UnixAddr{Name: sa.Name, Net: sotypeToNet(c.fd.sotype)}
}
}
return
@@ -151,7 +156,7 @@ func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAdd
switch sa := sa.(type) {
case *syscall.SockaddrUnix:
if sa.Name != "" {
- addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)}
+ addr = &UnixAddr{Name: sa.Name, Net: sotypeToNet(c.fd.sotype)}
}
}
return
@@ -337,9 +342,9 @@ func (l *UnixListener) SetDeadline(t time.Time) (err error) {
func (l *UnixListener) File() (f *os.File, err error) { return l.fd.dup() }
// ListenUnixgram listens for incoming Unix datagram packets addressed
-// to the local address laddr. The returned connection c's ReadFrom
-// and WriteTo methods can be used to receive and send packets with
-// per-packet addressing. The network net must be "unixgram".
+// to the local address laddr. The network net must be "unixgram".
+// The returned connection's ReadFrom and WriteTo methods can be used
+// to receive and send packets with per-packet addressing.
func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) {
switch net {
case "unixgram":
diff --git a/src/pkg/net/url/url.go b/src/pkg/net/url/url.go
index c1864036c..459dc473c 100644
--- a/src/pkg/net/url/url.go
+++ b/src/pkg/net/url/url.go
@@ -317,23 +317,22 @@ func getscheme(rawurl string) (scheme, path string, err error) {
// Maybe s is of the form t c u.
// If so, return t, c u (or t, u if cutc == true).
// If not, return s, "".
-func split(s string, c byte, cutc bool) (string, string) {
- for i := 0; i < len(s); i++ {
- if s[i] == c {
- if cutc {
- return s[0:i], s[i+1:]
- }
- return s[0:i], s[i:]
- }
+func split(s string, c string, cutc bool) (string, string) {
+ i := strings.Index(s, c)
+ if i < 0 {
+ return s, ""
+ }
+ if cutc {
+ return s[0:i], s[i+len(c):]
}
- return s, ""
+ return s[0:i], s[i:]
}
// Parse parses rawurl into a URL structure.
// The rawurl may be relative or absolute.
func Parse(rawurl string) (url *URL, err error) {
// Cut off #frag
- u, frag := split(rawurl, '#', true)
+ u, frag := split(rawurl, "#", true)
if url, err = parse(u, false); err != nil {
return nil, err
}
@@ -380,7 +379,7 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) {
}
url.Scheme = strings.ToLower(url.Scheme)
- rest, url.RawQuery = split(rest, '?', true)
+ rest, url.RawQuery = split(rest, "?", true)
if !strings.HasPrefix(rest, "/") {
if url.Scheme != "" {
@@ -396,7 +395,7 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) {
if (url.Scheme != "" || !viaRequest && !strings.HasPrefix(rest, "///")) && strings.HasPrefix(rest, "//") {
var authority string
- authority, rest = split(rest[2:], '/', false)
+ authority, rest = split(rest[2:], "/", false)
url.User, url.Host, err = parseAuthority(authority)
if err != nil {
goto Error
@@ -428,7 +427,7 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) {
}
user = User(userinfo)
} else {
- username, password := split(userinfo, ':', true)
+ username, password := split(userinfo, ":", true)
if username, err = unescape(username, encodeUserPassword); err != nil {
return
}
diff --git a/src/pkg/os/exec/exec_test.go b/src/pkg/os/exec/exec_test.go
index dfcf4be23..2467d29a1 100644
--- a/src/pkg/os/exec/exec_test.go
+++ b/src/pkg/os/exec/exec_test.go
@@ -25,7 +25,7 @@ func helperCommand(s ...string) *Cmd {
cs := []string{"-test.run=TestHelperProcess", "--"}
cs = append(cs, s...)
cmd := Command(os.Args[0], cs...)
- cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...)
+ cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
return cmd
}
diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go
index 9a4dd6c31..56cb315ad 100644
--- a/src/pkg/reflect/all_test.go
+++ b/src/pkg/reflect/all_test.go
@@ -14,6 +14,7 @@ import (
"os"
. "reflect"
"runtime"
+ "sort"
"sync"
"testing"
"time"
@@ -2199,6 +2200,30 @@ func TestPtrTo(t *testing.T) {
}
}
+func TestPtrToGC(t *testing.T) {
+ type T *uintptr
+ tt := TypeOf(T(nil))
+ pt := PtrTo(tt)
+ const n = 100
+ var x []interface{}
+ for i := 0; i < n; i++ {
+ v := New(pt)
+ p := new(*uintptr)
+ *p = new(uintptr)
+ **p = uintptr(i)
+ v.Elem().Set(ValueOf(p).Convert(pt))
+ x = append(x, v.Interface())
+ }
+ runtime.GC()
+
+ for i, xi := range x {
+ k := ValueOf(xi).Elem().Elem().Elem().Interface().(uintptr)
+ if k != uintptr(i) {
+ t.Errorf("lost x[%d] = %d, want %d", i, k, i)
+ }
+ }
+}
+
func TestAddr(t *testing.T) {
var p struct {
X, Y int
@@ -2991,8 +3016,10 @@ func TestSliceOf(t *testing.T) {
type T int
st := SliceOf(TypeOf(T(1)))
v := MakeSlice(st, 10, 10)
+ runtime.GC()
for i := 0; i < v.Len(); i++ {
v.Index(i).Set(ValueOf(T(i)))
+ runtime.GC()
}
s := fmt.Sprint(v.Interface())
want := "[0 1 2 3 4 5 6 7 8 9]"
@@ -3005,13 +3032,44 @@ func TestSliceOf(t *testing.T) {
checkSameType(t, Zero(SliceOf(TypeOf(T1(1)))).Interface(), []T1{})
}
+func TestSliceOfGC(t *testing.T) {
+ type T *uintptr
+ tt := TypeOf(T(nil))
+ st := SliceOf(tt)
+ const n = 100
+ var x []interface{}
+ for i := 0; i < n; i++ {
+ v := MakeSlice(st, n, n)
+ for j := 0; j < v.Len(); j++ {
+ p := new(uintptr)
+ *p = uintptr(i*n + j)
+ v.Index(j).Set(ValueOf(p).Convert(tt))
+ }
+ x = append(x, v.Interface())
+ }
+ runtime.GC()
+
+ for i, xi := range x {
+ v := ValueOf(xi)
+ for j := 0; j < v.Len(); j++ {
+ k := v.Index(j).Elem().Interface()
+ if k != uintptr(i*n+j) {
+ t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j)
+ }
+ }
+ }
+}
+
func TestChanOf(t *testing.T) {
// check construction and use of type not in binary
type T string
ct := ChanOf(BothDir, TypeOf(T("")))
v := MakeChan(ct, 2)
+ runtime.GC()
v.Send(ValueOf(T("hello")))
+ runtime.GC()
v.Send(ValueOf(T("world")))
+ runtime.GC()
sv1, _ := v.Recv()
sv2, _ := v.Recv()
@@ -3026,13 +3084,63 @@ func TestChanOf(t *testing.T) {
checkSameType(t, Zero(ChanOf(BothDir, TypeOf(T1(1)))).Interface(), (chan T1)(nil))
}
+func TestChanOfGC(t *testing.T) {
+ done := make(chan bool, 1)
+ go func() {
+ select {
+ case <-done:
+ case <-time.After(5 * time.Second):
+ panic("deadlock in TestChanOfGC")
+ }
+ }()
+
+ defer func() {
+ done <- true
+ }()
+
+ type T *uintptr
+ tt := TypeOf(T(nil))
+ ct := ChanOf(BothDir, tt)
+
+ // NOTE: The garbage collector handles allocated channels specially,
+ // so we have to save pointers to channels in x; the pointer code will
+ // use the gc info in the newly constructed chan type.
+ const n = 100
+ var x []interface{}
+ for i := 0; i < n; i++ {
+ v := MakeChan(ct, n)
+ for j := 0; j < n; j++ {
+ p := new(uintptr)
+ *p = uintptr(i*n + j)
+ v.Send(ValueOf(p).Convert(tt))
+ }
+ pv := New(ct)
+ pv.Elem().Set(v)
+ x = append(x, pv.Interface())
+ }
+ runtime.GC()
+
+ for i, xi := range x {
+ v := ValueOf(xi).Elem()
+ for j := 0; j < n; j++ {
+ pv, _ := v.Recv()
+ k := pv.Elem().Interface()
+ if k != uintptr(i*n+j) {
+ t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j)
+ }
+ }
+ }
+}
+
func TestMapOf(t *testing.T) {
// check construction and use of type not in binary
type K string
type V float64
v := MakeMap(MapOf(TypeOf(K("")), TypeOf(V(0))))
+ runtime.GC()
v.SetMapIndex(ValueOf(K("a")), ValueOf(V(1)))
+ runtime.GC()
s := fmt.Sprint(v.Interface())
want := "map[a:1]"
@@ -3042,6 +3150,81 @@ func TestMapOf(t *testing.T) {
// check that type already in binary is found
checkSameType(t, Zero(MapOf(TypeOf(V(0)), TypeOf(K("")))).Interface(), map[V]K(nil))
+
+ // check that invalid key type panics
+ shouldPanic(func() { MapOf(TypeOf((func())(nil)), TypeOf(false)) })
+}
+
+func TestMapOfGCKeys(t *testing.T) {
+ type T *uintptr
+ tt := TypeOf(T(nil))
+ mt := MapOf(tt, TypeOf(false))
+
+ // NOTE: The garbage collector handles allocated maps specially,
+ // so we have to save pointers to maps in x; the pointer code will
+ // use the gc info in the newly constructed map type.
+ const n = 100
+ var x []interface{}
+ for i := 0; i < n; i++ {
+ v := MakeMap(mt)
+ for j := 0; j < n; j++ {
+ p := new(uintptr)
+ *p = uintptr(i*n + j)
+ v.SetMapIndex(ValueOf(p).Convert(tt), ValueOf(true))
+ }
+ pv := New(mt)
+ pv.Elem().Set(v)
+ x = append(x, pv.Interface())
+ }
+ runtime.GC()
+
+ for i, xi := range x {
+ v := ValueOf(xi).Elem()
+ var out []int
+ for _, kv := range v.MapKeys() {
+ out = append(out, int(kv.Elem().Interface().(uintptr)))
+ }
+ sort.Ints(out)
+ for j, k := range out {
+ if k != i*n+j {
+ t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j)
+ }
+ }
+ }
+}
+
+func TestMapOfGCValues(t *testing.T) {
+ type T *uintptr
+ tt := TypeOf(T(nil))
+ mt := MapOf(TypeOf(1), tt)
+
+ // NOTE: The garbage collector handles allocated maps specially,
+ // so we have to save pointers to maps in x; the pointer code will
+ // use the gc info in the newly constructed map type.
+ const n = 100
+ var x []interface{}
+ for i := 0; i < n; i++ {
+ v := MakeMap(mt)
+ for j := 0; j < n; j++ {
+ p := new(uintptr)
+ *p = uintptr(i*n + j)
+ v.SetMapIndex(ValueOf(j), ValueOf(p).Convert(tt))
+ }
+ pv := New(mt)
+ pv.Elem().Set(v)
+ x = append(x, pv.Interface())
+ }
+ runtime.GC()
+
+ for i, xi := range x {
+ v := ValueOf(xi).Elem()
+ for j := 0; j < n; j++ {
+ k := v.MapIndex(ValueOf(j)).Elem().Interface().(uintptr)
+ if k != uintptr(i*n+j) {
+ t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j)
+ }
+ }
+ }
}
type B1 struct {
diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go
index 5ec94f576..b513fee90 100644
--- a/src/pkg/reflect/type.go
+++ b/src/pkg/reflect/type.go
@@ -233,17 +233,17 @@ const (
// with a unique tag like `reflect:"array"` or `reflect:"ptr"`
// so that code cannot convert from, say, *arrayType to *ptrType.
type rtype struct {
- size uintptr // size in bytes
- hash uint32 // hash of type; avoids computation in hash tables
- _ uint8 // unused/padding
- 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)
- gc uintptr // garbage collection data
- string *string // string form; unnecessary but undeniably useful
- *uncommonType // (relatively) uncommon fields
- ptrToThis *rtype // type for pointer to this type, if used in binary or has methods
+ size uintptr // size in bytes
+ hash uint32 // hash of type; avoids computation in hash tables
+ _ uint8 // unused/padding
+ 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)
+ gc unsafe.Pointer // garbage collection data
+ string *string // string form; unnecessary but undeniably useful
+ *uncommonType // (relatively) uncommon fields
+ ptrToThis *rtype // type for pointer to this type, if used in binary or has methods
}
// Method on non-interface type
@@ -345,6 +345,25 @@ type structType struct {
fields []structField // sorted by offset
}
+// NOTE: These are copied from ../runtime/mgc0.h.
+// They must be kept in sync.
+const (
+ _GC_END = iota
+ _GC_PTR
+ _GC_APTR
+ _GC_ARRAY_START
+ _GC_ARRAY_NEXT
+ _GC_CALL
+ _GC_MAP_PTR
+ _GC_CHAN_PTR
+ _GC_STRING
+ _GC_EFACE
+ _GC_IFACE
+ _GC_SLICE
+ _GC_REGION
+ _GC_NUM_INSTR
+)
+
/*
* The compiler knows the exact layout of all the data structures above.
* The compiler does not know about the data structures and methods below.
@@ -368,7 +387,10 @@ type Method struct {
// High bit says whether type has
// embedded pointers,to help garbage collector.
-const kindMask = 0x7f
+const (
+ kindMask = 0x7f
+ kindNoPointers = 0x80
+)
func (k Kind) String() string {
if int(k) < len(kindNames) {
@@ -978,6 +1000,32 @@ var ptrMap struct {
m map[*rtype]*ptrType
}
+// garbage collection bytecode program for pointer to memory without pointers.
+// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
+type ptrDataGC struct {
+ width uintptr // sizeof(ptr)
+ op uintptr // _GC_APTR
+ off uintptr // 0
+ end uintptr // _GC_END
+}
+
+var ptrDataGCProg = ptrDataGC{
+ width: unsafe.Sizeof((*byte)(nil)),
+ op: _GC_APTR,
+ off: 0,
+ end: _GC_END,
+}
+
+// garbage collection bytecode program for pointer to memory with pointers.
+// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
+type ptrGC struct {
+ width uintptr // sizeof(ptr)
+ op uintptr // _GC_PTR
+ off uintptr // 0
+ elemgc unsafe.Pointer // element gc type
+ end uintptr // _GC_END
+}
+
// PtrTo returns the pointer type with element t.
// For example, if t represents type Foo, PtrTo(t) represents *Foo.
func PtrTo(t Type) Type {
@@ -1034,6 +1082,20 @@ func (t *rtype) ptrTo() *rtype {
p.ptrToThis = nil
p.elem = t
+ if t.kind&kindNoPointers != 0 {
+ p.gc = unsafe.Pointer(&ptrDataGCProg)
+ } else {
+ p.gc = unsafe.Pointer(&ptrGC{
+ width: p.size,
+ op: _GC_PTR,
+ off: 0,
+ elemgc: t.gc,
+ end: _GC_END,
+ })
+ }
+ // INCORRECT. Uncomment to check that TestPtrToGC fails when p.gc is wrong.
+ //p.gc = unsafe.Pointer(&badGC{width: p.size, end: _GC_END})
+
ptrMap.m[t] = p
ptrMap.Unlock()
return &p.rtype
@@ -1338,6 +1400,21 @@ func cachePut(k cacheKey, t *rtype) Type {
return t
}
+// garbage collection bytecode program for chan or map.
+// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
+type chanMapGC struct {
+ width uintptr // sizeof(map)
+ op uintptr // _GC_MAP_PTR or _GC_CHAN_PTR
+ off uintptr // 0
+ typ *rtype // map type
+ end uintptr // _GC_END
+}
+
+type badGC struct {
+ width uintptr
+ end uintptr
+}
+
// ChanOf returns the channel type with the given direction and element type.
// For example, if t represents int, ChanOf(RecvDir, t) represents <-chan int.
//
@@ -1390,20 +1467,35 @@ func ChanOf(dir ChanDir, t Type) Type {
ch.uncommonType = nil
ch.ptrToThis = nil
+ ch.gc = unsafe.Pointer(&chanMapGC{
+ width: ch.size,
+ op: _GC_CHAN_PTR,
+ off: 0,
+ typ: &ch.rtype,
+ end: _GC_END,
+ })
+
+ // INCORRECT. Uncomment to check that TestChanOfGC fails when ch.gc is wrong.
+ //ch.gc = unsafe.Pointer(&badGC{width: ch.size, end: _GC_END})
+
return cachePut(ckey, &ch.rtype)
}
+func ismapkey(*rtype) bool // implemented in runtime
+
// MapOf returns the map type with the given key and element types.
// For example, if k represents int and e represents string,
// MapOf(k, e) represents map[int]string.
//
// If the key type is not a valid map key type (that is, if it does
-// not implement Go's == operator), MapOf panics. TODO(rsc).
+// not implement Go's == operator), MapOf panics.
func MapOf(key, elem Type) Type {
ktyp := key.(*rtype)
etyp := elem.(*rtype)
- // TODO: Check for invalid key types.
+ if !ismapkey(ktyp) {
+ panic("reflect.MapOf: invalid key type " + ktyp.String())
+ }
// Look in cache.
ckey := cacheKey{Map, ktyp, etyp, 0}
@@ -1432,9 +1524,47 @@ func MapOf(key, elem Type) Type {
mt.uncommonType = nil
mt.ptrToThis = nil
+ mt.gc = unsafe.Pointer(&chanMapGC{
+ width: mt.size,
+ op: _GC_MAP_PTR,
+ off: 0,
+ typ: &mt.rtype,
+ end: _GC_END,
+ })
+
+ // INCORRECT. Uncomment to check that TestMapOfGC and TestMapOfGCValues
+ // fail when mt.gc is wrong.
+ //mt.gc = unsafe.Pointer(&badGC{width: mt.size, end: _GC_END})
+
return cachePut(ckey, &mt.rtype)
}
+// garbage collection bytecode program for slice of non-zero-length values.
+// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
+type sliceGC struct {
+ width uintptr // sizeof(slice)
+ op uintptr // _GC_SLICE
+ off uintptr // 0
+ elemgc unsafe.Pointer // element gc program
+ end uintptr // _GC_END
+}
+
+// garbage collection bytecode program for slice of zero-length values.
+// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
+type sliceEmptyGC struct {
+ width uintptr // sizeof(slice)
+ op uintptr // _GC_APTR
+ off uintptr // 0
+ end uintptr // _GC_END
+}
+
+var sliceEmptyGCProg = sliceEmptyGC{
+ width: unsafe.Sizeof([]byte(nil)),
+ op: _GC_APTR,
+ off: 0,
+ end: _GC_END,
+}
+
// SliceOf returns the slice type with element type t.
// For example, if t represents int, SliceOf(t) represents []int.
func SliceOf(t Type) Type {
@@ -1466,6 +1596,21 @@ func SliceOf(t Type) Type {
slice.uncommonType = nil
slice.ptrToThis = nil
+ if typ.size == 0 {
+ slice.gc = unsafe.Pointer(&sliceEmptyGCProg)
+ } else {
+ slice.gc = unsafe.Pointer(&sliceGC{
+ width: slice.size,
+ op: _GC_SLICE,
+ off: 0,
+ elemgc: typ.gc,
+ end: _GC_END,
+ })
+ }
+
+ // INCORRECT. Uncomment to check that TestSliceOfOfGC fails when slice.gc is wrong.
+ //slice.gc = unsafe.Pointer(&badGC{width: slice.size, end: _GC_END})
+
return cachePut(ckey, &slice.rtype)
}
diff --git a/src/pkg/runtime/alg.c b/src/pkg/runtime/alg.c
index 2dc821256..a78d9780c 100644
--- a/src/pkg/runtime/alg.c
+++ b/src/pkg/runtime/alg.c
@@ -37,25 +37,11 @@ runtime·memhash(uintptr *h, uintptr s, void *a)
void
runtime·memequal(bool *eq, uintptr s, void *a, void *b)
{
- byte *ba, *bb, *aend;
-
if(a == b) {
*eq = 1;
return;
}
- ba = a;
- bb = b;
- aend = ba+s;
- while(ba != aend) {
- if(*ba != *bb) {
- *eq = 0;
- return;
- }
- ba++;
- bb++;
- }
- *eq = 1;
- return;
+ *eq = runtime·memeq(a, b, s);
}
void
@@ -323,6 +309,7 @@ void
runtime·strequal(bool *eq, uintptr s, void *a, void *b)
{
intgo alen;
+ byte *s1, *s2;
USED(s);
alen = ((String*)a)->len;
@@ -330,11 +317,13 @@ runtime·strequal(bool *eq, uintptr s, void *a, void *b)
*eq = false;
return;
}
- if(((String*)a)->str == ((String*)b)->str) {
+ s1 = ((String*)a)->str;
+ s2 = ((String*)b)->str;
+ if(s1 == s2) {
*eq = true;
return;
}
- runtime·memequal(eq, alen, ((String*)a)->str, ((String*)b)->str);
+ *eq = runtime·memeq(s1, s2, alen);
}
void
diff --git a/src/pkg/runtime/asm_386.s b/src/pkg/runtime/asm_386.s
index 6bcacf4cc..531057ff8 100644
--- a/src/pkg/runtime/asm_386.s
+++ b/src/pkg/runtime/asm_386.s
@@ -37,6 +37,8 @@ nocpuinfo:
MOVL _cgo_init(SB), AX
TESTL AX, AX
JZ needtls
+ MOVL $setmg_gcc<>(SB), BX
+ MOVL BX, 4(SP)
MOVL BP, 0(SP)
CALL AX
// skip runtime·ldt0setup(SB) and tls test after _cgo_init for non-windows
@@ -106,7 +108,7 @@ TEXT runtime·breakpoint(SB),7,$0
RET
TEXT runtime·asminit(SB),7,$0
- // Linux, Windows start the FPU in extended double precision.
+ // Linux and MinGW start the FPU in extended double precision.
// Other operating systems use double precision.
// Change to double precision to match them,
// and to match other hardware that only has double.
@@ -643,6 +645,15 @@ settls:
MOVL BX, g(CX)
RET
+// void setmg_gcc(M*, G*); set m and g. for use by gcc
+TEXT setmg_gcc<>(SB), 7, $0
+ get_tls(AX)
+ MOVL mm+0(FP), DX
+ MOVL DX, m(AX)
+ MOVL gg+4(FP), DX
+ MOVL DX,g (AX)
+ RET
+
// check that SP is in range [g->stackbase, g->stackguard)
TEXT runtime·stackcheck(SB), 7, $0
get_tls(CX)
@@ -975,3 +986,118 @@ TEXT shifts(SB),7,$0
LONG $0x0c0b0a09
LONG $0xff0f0e0d
+TEXT runtime·memeq(SB),7,$0
+ MOVL a+0(FP), SI
+ MOVL b+4(FP), DI
+ MOVL count+8(FP), BX
+ JMP runtime·memeqbody(SB)
+
+
+TEXT bytes·Equal(SB),7,$0
+ MOVL a_len+4(FP), BX
+ MOVL b_len+16(FP), CX
+ XORL AX, AX
+ CMPL BX, CX
+ JNE eqret
+ MOVL a+0(FP), SI
+ MOVL b+12(FP), DI
+ CALL runtime·memeqbody(SB)
+eqret:
+ MOVB AX, ret+24(FP)
+ RET
+
+// a in SI
+// b in DI
+// count in BX
+TEXT runtime·memeqbody(SB),7,$0
+ XORL AX, AX
+
+ CMPL BX, $4
+ JB small
+
+ // 64 bytes at a time using xmm registers
+hugeloop:
+ CMPL BX, $64
+ JB bigloop
+ TESTL $0x4000000, runtime·cpuid_edx(SB) // check for sse2
+ JE bigloop
+ MOVOU (SI), X0
+ MOVOU (DI), X1
+ MOVOU 16(SI), X2
+ MOVOU 16(DI), X3
+ MOVOU 32(SI), X4
+ MOVOU 32(DI), X5
+ MOVOU 48(SI), X6
+ MOVOU 48(DI), X7
+ PCMPEQB X1, X0
+ PCMPEQB X3, X2
+ PCMPEQB X5, X4
+ PCMPEQB X7, X6
+ PAND X2, X0
+ PAND X6, X4
+ PAND X4, X0
+ PMOVMSKB X0, DX
+ ADDL $64, SI
+ ADDL $64, DI
+ SUBL $64, BX
+ CMPL DX, $0xffff
+ JEQ hugeloop
+ RET
+
+ // 4 bytes at a time using 32-bit register
+bigloop:
+ CMPL BX, $4
+ JBE leftover
+ MOVL (SI), CX
+ MOVL (DI), DX
+ ADDL $4, SI
+ ADDL $4, DI
+ SUBL $4, BX
+ CMPL CX, DX
+ JEQ bigloop
+ RET
+
+ // remaining 0-4 bytes
+leftover:
+ MOVL -4(SI)(BX*1), CX
+ MOVL -4(DI)(BX*1), DX
+ CMPL CX, DX
+ SETEQ AX
+ RET
+
+small:
+ CMPL BX, $0
+ JEQ equal
+
+ LEAL 0(BX*8), CX
+ NEGL CX
+
+ MOVL SI, DX
+ CMPB DX, $0xfc
+ JA si_high
+
+ // load at SI won't cross a page boundary.
+ MOVL (SI), SI
+ JMP si_finish
+si_high:
+ // address ends in 111111xx. Load up to bytes we want, move to correct position.
+ MOVL -4(SI)(BX*1), SI
+ SHRL CX, SI
+si_finish:
+
+ // same for DI.
+ MOVL DI, DX
+ CMPB DX, $0xfc
+ JA di_high
+ MOVL (DI), DI
+ JMP di_finish
+di_high:
+ MOVL -4(DI)(BX*1), DI
+ SHRL CX, DI
+di_finish:
+
+ SUBL SI, DI
+ SHLL CX, DI
+equal:
+ SETEQ AX
+ RET
diff --git a/src/pkg/runtime/asm_amd64.s b/src/pkg/runtime/asm_amd64.s
index f4cfa576e..0dee1556d 100644
--- a/src/pkg/runtime/asm_amd64.s
+++ b/src/pkg/runtime/asm_amd64.s
@@ -37,6 +37,7 @@ nocpuinfo:
JZ needtls
// g0 already in DI
MOVQ DI, CX // Win64 uses CX for first parameter
+ MOVQ $setmg_gcc<>(SB), SI
CALL AX
CMPL runtime·iswindows(SB), $0
JEQ ok
@@ -682,6 +683,13 @@ settls:
MOVQ BX, g(CX)
RET
+// void setmg_gcc(M*, G*); set m and g called from gcc.
+TEXT setmg_gcc<>(SB),7,$0
+ get_tls(AX)
+ MOVQ DI, m(AX)
+ MOVQ SI, g(AX)
+ RET
+
// check that SP is in range [g->stackbase, g->stackguard)
TEXT runtime·stackcheck(SB), 7, $0
get_tls(CX)
@@ -899,3 +907,115 @@ TEXT shifts(SB),7,$0
QUAD $0xffff0f0e0d0c0b0a
QUAD $0x0807060504030201
QUAD $0xff0f0e0d0c0b0a09
+
+TEXT runtime·memeq(SB),7,$0
+ MOVQ a+0(FP), SI
+ MOVQ b+8(FP), DI
+ MOVQ count+16(FP), BX
+ JMP runtime·memeqbody(SB)
+
+
+TEXT bytes·Equal(SB),7,$0
+ MOVQ a_len+8(FP), BX
+ MOVQ b_len+32(FP), CX
+ XORQ AX, AX
+ CMPQ BX, CX
+ JNE eqret
+ MOVQ a+0(FP), SI
+ MOVQ b+24(FP), DI
+ CALL runtime·memeqbody(SB)
+eqret:
+ MOVB AX, ret+48(FP)
+ RET
+
+// a in SI
+// b in DI
+// count in BX
+TEXT runtime·memeqbody(SB),7,$0
+ XORQ AX, AX
+
+ CMPQ BX, $8
+ JB small
+
+ // 64 bytes at a time using xmm registers
+hugeloop:
+ CMPQ BX, $64
+ JB bigloop
+ MOVOU (SI), X0
+ MOVOU (DI), X1
+ MOVOU 16(SI), X2
+ MOVOU 16(DI), X3
+ MOVOU 32(SI), X4
+ MOVOU 32(DI), X5
+ MOVOU 48(SI), X6
+ MOVOU 48(DI), X7
+ PCMPEQB X1, X0
+ PCMPEQB X3, X2
+ PCMPEQB X5, X4
+ PCMPEQB X7, X6
+ PAND X2, X0
+ PAND X6, X4
+ PAND X4, X0
+ PMOVMSKB X0, DX
+ ADDQ $64, SI
+ ADDQ $64, DI
+ SUBQ $64, BX
+ CMPL DX, $0xffff
+ JEQ hugeloop
+ RET
+
+ // 8 bytes at a time using 64-bit register
+bigloop:
+ CMPQ BX, $8
+ JBE leftover
+ MOVQ (SI), CX
+ MOVQ (DI), DX
+ ADDQ $8, SI
+ ADDQ $8, DI
+ SUBQ $8, BX
+ CMPQ CX, DX
+ JEQ bigloop
+ RET
+
+ // remaining 0-8 bytes
+leftover:
+ MOVQ -8(SI)(BX*1), CX
+ MOVQ -8(DI)(BX*1), DX
+ CMPQ CX, DX
+ SETEQ AX
+ RET
+
+small:
+ CMPQ BX, $0
+ JEQ equal
+
+ LEAQ 0(BX*8), CX
+ NEGQ CX
+
+ CMPB SI, $0xf8
+ JA si_high
+
+ // load at SI won't cross a page boundary.
+ MOVQ (SI), SI
+ JMP si_finish
+si_high:
+ // address ends in 11111xxx. Load up to bytes we want, move to correct position.
+ MOVQ -8(SI)(BX*1), SI
+ SHRQ CX, SI
+si_finish:
+
+ // same for DI.
+ CMPB DI, $0xf8
+ JA di_high
+ MOVQ (DI), DI
+ JMP di_finish
+di_high:
+ MOVQ -8(DI)(BX*1), DI
+ SHRQ CX, DI
+di_finish:
+
+ SUBQ SI, DI
+ SHLQ CX, DI
+equal:
+ SETEQ AX
+ RET
diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s
index 6b2d6afda..ee9acb749 100644
--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -326,7 +326,7 @@ TEXT runtime·cgocallback(SB),7,$12
// cgocallback_gofunc(void (*fn)(void*), void *frame, uintptr framesize)
// See cgocall.c for more details.
-TEXT runtime·cgocallback_gofunc(SB),7,$16
+TEXT runtime·cgocallback_gofunc(SB),7,$12
// Load m and g from thread-local storage.
MOVW _cgo_load_gm(SB), R0
CMP $0, R0
@@ -337,7 +337,7 @@ TEXT runtime·cgocallback_gofunc(SB),7,$16
// In this case, we're running on the thread stack, so there's
// lots of space, but the linker doesn't know. Hide the call from
// the linker analysis by using an indirect call.
- MOVW m, savedm-16(SP)
+ MOVW m, savedm-12(SP)
CMP $0, m
B.NE havem
MOVW $runtime·needm(SB), R0
@@ -348,10 +348,6 @@ havem:
// Save current m->g0->sched.sp on stack and then set it to SP.
// Save current sp in m->g0->sched.sp in preparation for
// switch back to m->curg stack.
- MOVW fn+0(FP), R0
- MOVW frame+4(FP), R1
- MOVW framesize+8(FP), R2
-
MOVW m_g0(m), R3
MOVW (g_sched+gobuf_sp)(R3), R4
MOVW.W R4, -4(R13)
@@ -368,23 +364,23 @@ havem:
// This has the added benefit that it looks to the traceback
// routine like cgocallbackg is going to return to that
// PC (because we defined cgocallbackg to have
- // a frame size of 16, the same amount that we use below),
+ // a frame size of 12, the same amount that we use below),
// so that the traceback will seamlessly trace back into
// the earlier calls.
+ MOVW fn+4(FP), R0
+ MOVW frame+8(FP), R1
+ MOVW framesize+12(FP), R2
- // Save current m->g0->sched.sp on stack and then set it to SP.
MOVW m_curg(m), g
MOVW (g_sched+gobuf_sp)(g), R4 // prepare stack as R4
// Push gobuf.pc
MOVW (g_sched+gobuf_pc)(g), R5
- SUB $4, R4
- MOVW R5, 0(R4)
+ MOVW.W R5, -16(R4)
// Push arguments to cgocallbackg.
// Frame size here must match the frame size above
// to trick traceback routines into doing the right thing.
- SUB $16, R4
MOVW R0, 4(R4)
MOVW R1, 8(R4)
MOVW R2, 12(R4)
@@ -394,10 +390,10 @@ havem:
BL runtime·cgocallbackg(SB)
// Restore g->gobuf (== m->curg->gobuf) from saved values.
- MOVW 16(R13), R5
+ MOVW 0(R13), R5
MOVW R5, (g_sched+gobuf_pc)(g)
- ADD $(16+4), R13 // SP clobbered! It is ok!
- MOVW R13, (g_sched+gobuf_sp)(g)
+ ADD $(12+4), R13, R4
+ MOVW R4, (g_sched+gobuf_sp)(g)
// Switch back to m->g0's stack and restore m->g0->sched.sp.
// (Unlike m->curg, the g0 goroutine never uses sched.pc,
@@ -411,7 +407,7 @@ havem:
// If the m on entry was nil, we called needm above to borrow an m
// for the duration of the call. Since the call is over, return it with dropm.
- MOVW savedm-16(SP), R6
+ MOVW savedm-12(SP), R6
CMP $0, R6
B.NE 3(PC)
MOVW $runtime·dropm(SB), R0
@@ -491,7 +487,7 @@ TEXT runtime·stackguard(SB),7,$0
MOVW R2, limit+4(FP)
RET
-// not implemented for ARM
+// AES hashing not implemented for ARM
TEXT runtime·aeshash(SB),7,$-4
MOVW $0, R0
MOVW (R0), R1
@@ -504,3 +500,20 @@ TEXT runtime·aeshash64(SB),7,$-4
TEXT runtime·aeshashstr(SB),7,$-4
MOVW $0, R0
MOVW (R0), R1
+
+TEXT runtime·memeq(SB),7,$-4
+ MOVW a+0(FP), R1
+ MOVW b+4(FP), R2
+ MOVW n+8(FP), R3
+ ADD R1, R3, R6
+ MOVW $1, R0
+_next:
+ CMP R1, R6
+ RET.EQ
+ MOVBU.P 1(R1), R4
+ MOVBU.P 1(R2), R5
+ CMP R4, R5
+ BEQ _next
+
+ MOVW $0, R0
+ RET
diff --git a/src/pkg/runtime/cgo/gcc_freebsd_386.c b/src/pkg/runtime/cgo/gcc_freebsd_386.c
index 7c62a1bc4..6797824c6 100644
--- a/src/pkg/runtime/cgo/gcc_freebsd_386.c
+++ b/src/pkg/runtime/cgo/gcc_freebsd_386.c
@@ -10,13 +10,15 @@
#include "libcgo.h"
static void* threadentry(void*);
+static void (*setmg_gcc)(void*, void*);
void
-x_cgo_init(G *g)
+x_cgo_init(G *g, void (*setmg)(void*, void*))
{
pthread_attr_t attr;
size_t size;
+ setmg_gcc = setmg;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
g->stackguard = (uintptr)&attr - size + 4096;
@@ -66,15 +68,9 @@ threadentry(void *v)
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
/*
- * Set specific keys. On FreeBSD/ELF, the thread local storage
- * is just before %gs:0. Our dynamic 8.out's reserve 8 bytes
- * for the two words g and m at %gs:-8 and %gs:-4.
+ * Set specific keys.
*/
- asm volatile (
- "movl %0, %%gs:-8\n" // MOVL g, -8(GS)
- "movl %1, %%gs:-4\n" // MOVL m, -4(GS)
- :: "r"(ts.g), "r"(ts.m)
- );
+ setmg_gcc((void*)ts.m, (void*)ts.g);
crosscall_386(ts.fn);
return nil;
diff --git a/src/pkg/runtime/cgo/gcc_freebsd_amd64.c b/src/pkg/runtime/cgo/gcc_freebsd_amd64.c
index 6be8bd251..eb342a2ff 100644
--- a/src/pkg/runtime/cgo/gcc_freebsd_amd64.c
+++ b/src/pkg/runtime/cgo/gcc_freebsd_amd64.c
@@ -10,20 +10,21 @@
#include "libcgo.h"
static void* threadentry(void*);
+static void (*setmg_gcc)(void*, void*);
void
-x_cgo_init(G *g)
+x_cgo_init(G *g, void (*setmg)(void*, void*))
{
pthread_attr_t attr;
size_t size;
+ setmg_gcc = setmg;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
g->stackguard = (uintptr)&attr - size + 4096;
pthread_attr_destroy(&attr);
}
-
void
_cgo_sys_thread_start(ThreadStart *ts)
{
@@ -67,15 +68,10 @@ threadentry(void *v)
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
/*
- * Set specific keys. On FreeBSD/ELF, the thread local storage
- * is just before %fs:0. Our dynamic 6.out's reserve 16 bytes
- * for the two words g and m at %fs:-16 and %fs:-8.
+ * Set specific keys.
*/
- asm volatile (
- "movq %0, %%fs:-16\n" // MOVL g, -16(FS)
- "movq %1, %%fs:-8\n" // MOVL m, -8(FS)
- :: "r"(ts.g), "r"(ts.m)
- );
+ setmg_gcc((void*)ts.m, (void*)ts.g);
+
crosscall_amd64(ts.fn);
return nil;
}
diff --git a/src/pkg/runtime/cgo/gcc_linux_386.c b/src/pkg/runtime/cgo/gcc_linux_386.c
index 9357a63f7..c25c7b70f 100644
--- a/src/pkg/runtime/cgo/gcc_linux_386.c
+++ b/src/pkg/runtime/cgo/gcc_linux_386.c
@@ -8,13 +8,15 @@
#include "libcgo.h"
static void *threadentry(void*);
+static void (*setmg_gcc)(void*, void*);
void
-x_cgo_init(G *g)
+x_cgo_init(G *g, void (*setmg)(void*, void*))
{
pthread_attr_t attr;
size_t size;
+ setmg_gcc = setmg;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
g->stackguard = (uintptr)&attr - size + 4096;
@@ -69,18 +71,9 @@ threadentry(void *v)
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
/*
- * Set specific keys. On Linux/ELF, the thread local storage
- * is just before %gs:0. Our dynamic 8.out's reserve 8 bytes
- * for the two words g and m at %gs:-8 and %gs:-4.
- * Xen requires us to access those words indirect from %gs:0
- * which points at itself.
+ * Set specific keys.
*/
- asm volatile (
- "movl %%gs:0, %%eax\n" // MOVL 0(GS), tmp
- "movl %0, -8(%%eax)\n" // MOVL g, -8(GS)
- "movl %1, -4(%%eax)\n" // MOVL m, -4(GS)
- :: "r"(ts.g), "r"(ts.m) : "%eax"
- );
+ setmg_gcc((void*)ts.m, (void*)ts.g);
crosscall_386(ts.fn);
return nil;
diff --git a/src/pkg/runtime/cgo/gcc_linux_amd64.c b/src/pkg/runtime/cgo/gcc_linux_amd64.c
index bc76117d3..bd7c88d99 100644
--- a/src/pkg/runtime/cgo/gcc_linux_amd64.c
+++ b/src/pkg/runtime/cgo/gcc_linux_amd64.c
@@ -8,13 +8,15 @@
#include "libcgo.h"
static void* threadentry(void*);
+static void (*setmg_gcc)(void*, void*);
void
-x_cgo_init(G* g)
+x_cgo_init(G* g, void (*setmg)(void*, void*))
{
pthread_attr_t attr;
size_t size;
+ setmg_gcc = setmg;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
g->stackguard = (uintptr)&attr - size + 4096;
@@ -64,15 +66,10 @@ threadentry(void *v)
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
/*
- * Set specific keys. On Linux/ELF, the thread local storage
- * is just before %fs:0. Our dynamic 6.out's reserve 16 bytes
- * for the two words g and m at %fs:-16 and %fs:-8.
+ * Set specific keys.
*/
- asm volatile (
- "movq %0, %%fs:-16\n" // MOVL g, -16(FS)
- "movq %1, %%fs:-8\n" // MOVL m, -8(FS)
- :: "r"(ts.g), "r"(ts.m)
- );
+ setmg_gcc((void*)ts.m, (void*)ts.g);
+
crosscall_amd64(ts.fn);
return nil;
}
diff --git a/src/pkg/runtime/cgo/gcc_netbsd_386.c b/src/pkg/runtime/cgo/gcc_netbsd_386.c
index 09b271df4..b399e16dc 100644
--- a/src/pkg/runtime/cgo/gcc_netbsd_386.c
+++ b/src/pkg/runtime/cgo/gcc_netbsd_386.c
@@ -9,13 +9,15 @@
#include "libcgo.h"
static void* threadentry(void*);
+static void (*setmg_gcc)(void*, void*);
void
-x_cgo_init(G *g)
+x_cgo_init(G *g, void (*setmg)(void*, void*))
{
pthread_attr_t attr;
size_t size;
+ setmg_gcc = setmg;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
g->stackguard = (uintptr)&attr - size + 4096;
@@ -65,15 +67,9 @@ threadentry(void *v)
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
/*
- * Set specific keys. On NetBSD/ELF, the thread local storage
- * is just before %gs:0. Our dynamic 8.out's reserve 8 bytes
- * for the two words g and m at %gs:-8 and %gs:-4.
+ * Set specific keys.
*/
- asm volatile (
- "movl %0, %%gs:-8\n" // MOVL g, -8(GS)
- "movl %1, %%gs:-4\n" // MOVL m, -4(GS)
- :: "r"(ts.g), "r"(ts.m)
- );
+ setmg_gcc((void*)ts.m, (void*)ts.g);
crosscall_386(ts.fn);
return nil;
diff --git a/src/pkg/runtime/cgo/gcc_netbsd_amd64.c b/src/pkg/runtime/cgo/gcc_netbsd_amd64.c
index 080c59ba4..f27e142ce 100644
--- a/src/pkg/runtime/cgo/gcc_netbsd_amd64.c
+++ b/src/pkg/runtime/cgo/gcc_netbsd_amd64.c
@@ -9,13 +9,15 @@
#include "libcgo.h"
static void* threadentry(void*);
+static void (*setmg_gcc)(void*, void*);
void
-x_cgo_init(G *g)
+x_cgo_init(G *g, void (*setmg)(void*, void*))
{
pthread_attr_t attr;
size_t size;
+ setmg_gcc = setmg;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
g->stackguard = (uintptr)&attr - size + 4096;
@@ -66,15 +68,10 @@ threadentry(void *v)
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
/*
- * Set specific keys. On NetBSD/ELF, the thread local storage
- * is just before %fs:0. Our dynamic 6.out's reserve 16 bytes
- * for the two words g and m at %fs:-16 and %fs:-8.
+ * Set specific keys.
*/
- asm volatile (
- "movq %0, %%fs:-16\n" // MOVL g, -16(FS)
- "movq %1, %%fs:-8\n" // MOVL m, -8(FS)
- :: "r"(ts.g), "r"(ts.m)
- );
+ setmg_gcc((void*)ts.m, (void*)ts.g);
+
crosscall_amd64(ts.fn);
return nil;
}
diff --git a/src/pkg/runtime/cgo/gcc_openbsd_386.c b/src/pkg/runtime/cgo/gcc_openbsd_386.c
index 80be31b9c..6422d1b93 100644
--- a/src/pkg/runtime/cgo/gcc_openbsd_386.c
+++ b/src/pkg/runtime/cgo/gcc_openbsd_386.c
@@ -11,6 +11,7 @@
#include "libcgo.h"
static void* threadentry(void*);
+static void (*setmg_gcc)(void*, void*);
// TCB_SIZE is sizeof(struct thread_control_block),
// as defined in /usr/src/lib/librthread/tcb.h
@@ -82,12 +83,13 @@ pthread_create(pthread_t *thread, const pthread_attr_t *attr,
}
void
-x_cgo_init(G *g)
+x_cgo_init(G *g, void (*setmg)(void*, void*))
{
pthread_attr_t attr;
size_t size;
void *handle;
+ setmg_gcc = setmg;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
g->stackguard = (uintptr)&attr - size + 4096;
@@ -154,15 +156,9 @@ threadentry(void *v)
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
/*
- * Set specific keys. On OpenBSD/ELF, the thread local storage
- * is just before %gs:0. Our dynamic 8.out's reserve 8 bytes
- * for the two words g and m at %gs:-8 and %gs:-4.
+ * Set specific keys.
*/
- asm volatile (
- "movl %0, %%gs:-8\n" // MOVL g, -8(GS)
- "movl %1, %%gs:-4\n" // MOVL m, -4(GS)
- :: "r"(ts.g), "r"(ts.m)
- );
+ setmg_gcc((void*)ts.m, (void*)ts.g);
crosscall_386(ts.fn);
return nil;
diff --git a/src/pkg/runtime/cgo/gcc_openbsd_amd64.c b/src/pkg/runtime/cgo/gcc_openbsd_amd64.c
index e9cc8184b..5a5a17114 100644
--- a/src/pkg/runtime/cgo/gcc_openbsd_amd64.c
+++ b/src/pkg/runtime/cgo/gcc_openbsd_amd64.c
@@ -11,6 +11,7 @@
#include "libcgo.h"
static void* threadentry(void*);
+static void (*setmg_gcc)(void*, void*);
// TCB_SIZE is sizeof(struct thread_control_block),
// as defined in /usr/src/lib/librthread/tcb.h
@@ -82,12 +83,13 @@ pthread_create(pthread_t *thread, const pthread_attr_t *attr,
}
void
-x_cgo_init(G *g)
+x_cgo_init(G *g, void (*setmg)(void*, void*))
{
pthread_attr_t attr;
size_t size;
void *handle;
+ setmg_gcc = setmg;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
g->stackguard = (uintptr)&attr - size + 4096;
@@ -155,15 +157,10 @@ threadentry(void *v)
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
/*
- * Set specific keys. On OpenBSD/ELF, the thread local storage
- * is just before %fs:0. Our dynamic 6.out's reserve 16 bytes
- * for the two words g and m at %fs:-16 and %fs:-8.
+ * Set specific keys.
*/
- asm volatile (
- "movq %0, %%fs:-16\n" // MOVL g, -16(FS)
- "movq %1, %%fs:-8\n" // MOVL m, -8(FS)
- :: "r"(ts.g), "r"(ts.m)
- );
+ setmg_gcc((void*)ts.m, (void*)ts.g);
+
crosscall_amd64(ts.fn);
return nil;
}
diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c
index 0c9618749..b82966546 100644
--- a/src/pkg/runtime/cgocall.c
+++ b/src/pkg/runtime/cgocall.c
@@ -98,6 +98,15 @@ static void unwindm(void);
static void endcgo(void);
static FuncVal endcgoV = { endcgo };
+// Gives a hint that the next syscall
+// executed by the current goroutine will block.
+// Currently used only on windows.
+void
+net·runtime_blockingSyscallHint(void)
+{
+ g->blockingsyscall = true;
+}
+
void
runtime·cgocall(void (*fn)(void*), void *arg)
{
@@ -145,7 +154,11 @@ runtime·cgocall(void (*fn)(void*), void *arg)
* so it is safe to call while "in a system call", outside
* the $GOMAXPROCS accounting.
*/
- runtime·entersyscall();
+ if(g->blockingsyscall) {
+ g->blockingsyscall = false;
+ runtime·entersyscallblock();
+ } else
+ runtime·entersyscall();
runtime·asmcgocall(fn, arg);
runtime·exitsyscall();
diff --git a/src/pkg/runtime/gc_test.go b/src/pkg/runtime/gc_test.go
index 3475339bf..26fc77de1 100644
--- a/src/pkg/runtime/gc_test.go
+++ b/src/pkg/runtime/gc_test.go
@@ -13,7 +13,7 @@ import (
func TestGcSys(t *testing.T) {
if os.Getenv("GOGC") == "off" {
- t.Fatalf("GOGC=off in environment; test cannot pass")
+ t.Skip("skipping test; GOGC=off in environment")
}
data := struct{ Short bool }{testing.Short()}
got := executeTest(t, testGCSysSource, &data)
diff --git a/src/pkg/runtime/hashmap.c b/src/pkg/runtime/hashmap.c
index 0f92becab..4ebfffd6c 100644
--- a/src/pkg/runtime/hashmap.c
+++ b/src/pkg/runtime/hashmap.c
@@ -74,9 +74,9 @@
typedef struct Bucket Bucket;
struct Bucket
{
- uint8 tophash[BUCKETSIZE]; // top 8 bits of hash of each entry (0 = empty)
+ uint8 tophash[BUCKETSIZE]; // top 8 bits of hash of each entry (0 = empty)
Bucket *overflow; // overflow bucket, if any
- byte data[1]; // BUCKETSIZE keys followed by BUCKETSIZE values
+ byte data[1]; // BUCKETSIZE keys followed by BUCKETSIZE values
};
// NOTE: packing all the keys together and then all the values together makes the
// code a bit more complicated than alternating key/value/key/value/... but it allows
@@ -102,7 +102,7 @@ struct Hmap
uint16 bucketsize; // bucket size in bytes
uintptr hash0; // hash seed
- byte *buckets; // array of 2^B Buckets
+ byte *buckets; // array of 2^B Buckets. may be nil if count==0.
byte *oldbuckets; // previous bucket array of half the size, non-nil only when growing
uintptr nevacuate; // progress counter for evacuation (buckets less than this have been evacuated)
};
@@ -126,6 +126,7 @@ enum
{
docheck = 0, // check invariants before and after every op. Slow!!!
debug = 0, // print every operation
+ checkgc = 0 || docheck, // check interaction of mallocgc() with the garbage collector
};
static void
check(MapType *t, Hmap *h)
@@ -221,7 +222,7 @@ hash_init(MapType *t, Hmap *h, uint32 hint)
keysize = sizeof(byte*);
}
valuesize = t->elem->size;
- if(valuesize >= MAXVALUESIZE) {
+ if(valuesize > MAXVALUESIZE) {
flags |= IndirectValue;
valuesize = sizeof(byte*);
}
@@ -253,10 +254,16 @@ hash_init(MapType *t, Hmap *h, uint32 hint)
// allocate initial hash table
// If hint is large zeroing this memory could take a while.
- buckets = runtime·mallocgc(bucketsize << B, 0, 1, 0);
- for(i = 0; i < (uintptr)1 << B; i++) {
- b = (Bucket*)(buckets + i * bucketsize);
- clearbucket(b);
+ if(checkgc) mstats.next_gc = mstats.heap_alloc;
+ if(B == 0) {
+ // done lazily later.
+ buckets = nil;
+ } else {
+ buckets = runtime·mallocgc(bucketsize << B, 0, 1, 0);
+ for(i = 0; i < (uintptr)1 << B; i++) {
+ b = (Bucket*)(buckets + i * bucketsize);
+ clearbucket(b);
+ }
}
// initialize Hmap
@@ -322,6 +329,7 @@ evacuate(MapType *t, Hmap *h, uintptr oldbucket)
// the B'th bit of the hash in this case.
if((hash & newbit) == 0) {
if(xi == BUCKETSIZE) {
+ if(checkgc) mstats.next_gc = mstats.heap_alloc;
newx = runtime·mallocgc(h->bucketsize, 0, 1, 0);
clearbucket(newx);
x->overflow = newx;
@@ -346,6 +354,7 @@ evacuate(MapType *t, Hmap *h, uintptr oldbucket)
xv += h->valuesize;
} else {
if(yi == BUCKETSIZE) {
+ if(checkgc) mstats.next_gc = mstats.heap_alloc;
newy = runtime·mallocgc(h->bucketsize, 0, 1, 0);
clearbucket(newy);
y->overflow = newy;
@@ -441,6 +450,7 @@ hash_grow(MapType *t, Hmap *h)
runtime·throw("evacuation not done in time");
old_buckets = h->buckets;
// NOTE: this could be a big malloc, but since we don't need zeroing it is probably fast.
+ if(checkgc) mstats.next_gc = mstats.heap_alloc;
new_buckets = runtime·mallocgc(h->bucketsize << (h->B + 1), 0, 1, 0);
flags = (h->flags & ~(Iterator | OldIterator));
if((h->flags & Iterator) != 0) {
@@ -470,7 +480,7 @@ hash_lookup(MapType *t, Hmap *h, byte **keyp)
{
void *key;
uintptr hash;
- uintptr bucket;
+ uintptr bucket, oldbucket;
Bucket *b;
uint8 top;
uintptr i;
@@ -480,12 +490,20 @@ hash_lookup(MapType *t, Hmap *h, byte **keyp)
key = *keyp;
if(docheck)
check(t, h);
+ if(h->count == 0)
+ return nil;
hash = h->hash0;
t->key->alg->hash(&hash, t->key->size, key);
bucket = hash & (((uintptr)1 << h->B) - 1);
- if(h->oldbuckets != nil)
- grow_work(t, h, bucket);
- b = (Bucket*)(h->buckets + bucket * h->bucketsize);
+ if(h->oldbuckets != nil) {
+ oldbucket = bucket & (((uintptr)1 << (h->B - 1)) - 1);
+ b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
+ if(evacuated(b)) {
+ b = (Bucket*)(h->buckets + bucket * h->bucketsize);
+ }
+ } else {
+ b = (Bucket*)(h->buckets + bucket * h->bucketsize);
+ }
top = hash >> (sizeof(uintptr)*8 - 8);
if(top == 0)
top = 1;
@@ -509,12 +527,14 @@ hash_lookup(MapType *t, Hmap *h, byte **keyp)
static uint8 empty_value[MAXVALUESIZE];
// Specialized versions of mapaccess1 for specific types.
-// See ./hashmap_fast and ../../cmd/gc/walk.c.
+// See ./hashmap_fast.c and ../../cmd/gc/walk.c.
#define HASH_LOOKUP1 runtime·mapaccess1_fast32
#define HASH_LOOKUP2 runtime·mapaccess2_fast32
#define KEYTYPE uint32
#define HASHFUNC runtime·algarray[AMEM32].hash
#define EQFUNC(x,y) ((x) == (y))
+#define EQMAYBE(x,y) ((x) == (y))
+#define HASMAYBE false
#define QUICKEQ(x) true
#include "hashmap_fast.c"
@@ -523,6 +543,8 @@ static uint8 empty_value[MAXVALUESIZE];
#undef KEYTYPE
#undef HASHFUNC
#undef EQFUNC
+#undef EQMAYBE
+#undef HASMAYBE
#undef QUICKEQ
#define HASH_LOOKUP1 runtime·mapaccess1_fast64
@@ -530,6 +552,8 @@ static uint8 empty_value[MAXVALUESIZE];
#define KEYTYPE uint64
#define HASHFUNC runtime·algarray[AMEM64].hash
#define EQFUNC(x,y) ((x) == (y))
+#define EQMAYBE(x,y) ((x) == (y))
+#define HASMAYBE false
#define QUICKEQ(x) true
#include "hashmap_fast.c"
@@ -538,13 +562,17 @@ static uint8 empty_value[MAXVALUESIZE];
#undef KEYTYPE
#undef HASHFUNC
#undef EQFUNC
+#undef EQMAYBE
+#undef HASMAYBE
#undef QUICKEQ
#define HASH_LOOKUP1 runtime·mapaccess1_faststr
#define HASH_LOOKUP2 runtime·mapaccess2_faststr
#define KEYTYPE String
#define HASHFUNC runtime·algarray[ASTRING].hash
-#define EQFUNC(x,y) ((x).len == (y).len && ((x).str == (y).str || runtime·mcmp((x).str, (y).str, (x).len) == 0))
+#define EQFUNC(x,y) ((x).len == (y).len && ((x).str == (y).str || runtime·memeq((x).str, (y).str, (x).len)))
+#define EQMAYBE(x,y) ((x).len == (y).len)
+#define HASMAYBE true
#define QUICKEQ(x) ((x).len < 32)
#include "hashmap_fast.c"
@@ -567,6 +595,12 @@ hash_insert(MapType *t, Hmap *h, void *key, void *value)
check(t, h);
hash = h->hash0;
t->key->alg->hash(&hash, t->key->size, key);
+ if(h->buckets == nil) {
+ h->buckets = runtime·mallocgc(h->bucketsize, 0, 1, 0);
+ b = (Bucket*)(h->buckets);
+ clearbucket(b);
+ }
+
again:
bucket = hash & (((uintptr)1 << h->B) - 1);
if(h->oldbuckets != nil)
@@ -611,6 +645,7 @@ hash_insert(MapType *t, Hmap *h, void *key, void *value)
if(inserti == nil) {
// all current buckets are full, allocate a new one.
+ if(checkgc) mstats.next_gc = mstats.heap_alloc;
newb = runtime·mallocgc(h->bucketsize, 0, 1, 0);
clearbucket(newb);
b->overflow = newb;
@@ -621,11 +656,13 @@ hash_insert(MapType *t, Hmap *h, void *key, void *value)
// store new key/value at insert position
if((h->flags & IndirectKey) != 0) {
+ if(checkgc) mstats.next_gc = mstats.heap_alloc;
kmem = runtime·mallocgc(t->key->size, 0, 1, 0);
*(byte**)insertk = kmem;
insertk = kmem;
}
if((h->flags & IndirectValue) != 0) {
+ if(checkgc) mstats.next_gc = mstats.heap_alloc;
vmem = runtime·mallocgc(t->elem->size, 0, 1, 0);
*(byte**)insertv = vmem;
insertv = vmem;
@@ -651,6 +688,8 @@ hash_remove(MapType *t, Hmap *h, void *key)
if(docheck)
check(t, h);
+ if(h->count == 0)
+ return;
hash = h->hash0;
t->key->alg->hash(&hash, t->key->size, key);
bucket = hash & (((uintptr)1 << h->B) - 1);
@@ -718,6 +757,7 @@ struct hash_iter
uintptr bucket;
struct Bucket *bptr;
uintptr i;
+ intptr check_bucket;
};
// iterator state:
@@ -727,6 +767,9 @@ struct hash_iter
static void
hash_iter_init(MapType *t, Hmap *h, struct hash_iter *it)
{
+ if(sizeof(struct hash_iter) / sizeof(uintptr) != 11) {
+ runtime·throw("hash_iter size incorrect"); // see ../../cmd/gc/range.c
+ }
it->t = t;
it->h = h;
@@ -739,8 +782,14 @@ hash_iter_init(MapType *t, Hmap *h, struct hash_iter *it)
it->wrapped = false;
it->bptr = nil;
- // Remember we have an iterator at this level.
- h->flags |= Iterator;
+ // Remember we have an iterator.
+ h->flags |= Iterator | OldIterator; // careful: see issue 5120.
+
+ if(h->buckets == nil) {
+ // Empty map. Force next hash_next to exit without
+ // evalulating h->bucket.
+ it->wrapped = true;
+ }
}
// initializes it->key and it->value to the next key/value pair
@@ -750,9 +799,11 @@ hash_next(struct hash_iter *it)
{
Hmap *h;
MapType *t;
- uintptr bucket;
+ uintptr bucket, oldbucket;
+ uintptr hash;
Bucket *b;
uintptr i;
+ intptr check_bucket;
bool eq;
byte *k, *v;
byte *rk, *rv;
@@ -762,6 +813,7 @@ hash_next(struct hash_iter *it)
bucket = it->bucket;
b = it->bptr;
i = it->i;
+ check_bucket = it->check_bucket;
next:
if(b == nil) {
@@ -773,10 +825,21 @@ next:
}
if(h->oldbuckets != nil && it->B == h->B) {
// Iterator was started in the middle of a grow, and the grow isn't done yet.
- // Make sure the bucket we're about to read is valid.
- grow_work(t, h, bucket);
+ // If the bucket we're looking at hasn't been filled in yet (i.e. the old
+ // bucket hasn't been evacuated) then we need to iterate through the old
+ // bucket and only return the ones that will be migrated to this bucket.
+ oldbucket = bucket & (((uintptr)1 << (it->B - 1)) - 1);
+ b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
+ if(!evacuated(b)) {
+ check_bucket = bucket;
+ } else {
+ b = (Bucket*)(it->buckets + bucket * h->bucketsize);
+ check_bucket = -1;
+ }
+ } else {
+ b = (Bucket*)(it->buckets + bucket * h->bucketsize);
+ check_bucket = -1;
}
- b = (Bucket*)(it->buckets + bucket * h->bucketsize);
bucket++;
if(bucket == ((uintptr)1 << it->B)) {
bucket = 0;
@@ -788,6 +851,30 @@ next:
v = b->data + h->keysize * BUCKETSIZE + h->valuesize * i;
for(; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) {
if(b->tophash[i] != 0) {
+ if(check_bucket >= 0) {
+ // Special case: iterator was started during a grow and the
+ // grow is not done yet. We're working on a bucket whose
+ // oldbucket has not been evacuated yet. So we iterate
+ // through the oldbucket, skipping any keys that will go
+ // to the other new bucket (each oldbucket expands to two
+ // buckets during a grow).
+ t->key->alg->equal(&eq, t->key->size, IK(h, k), IK(h, k));
+ if(!eq) {
+ // Hash is meaningless if k != k (NaNs). Return all
+ // NaNs during the first of the two new buckets.
+ if(bucket >= ((uintptr)1 << (it->B - 1))) {
+ continue;
+ }
+ } else {
+ // If the item in the oldbucket is not destined for
+ // the current new bucket in the iteration, skip it.
+ hash = h->hash0;
+ t->key->alg->hash(&hash, t->key->size, IK(h, k));
+ if((hash & (((uintptr)1 << it->B) - 1)) != check_bucket) {
+ continue;
+ }
+ }
+ }
if(!evacuated(b)) {
// this is the golden data, we can return it.
it->key = IK(h, k);
@@ -820,6 +907,7 @@ next:
it->bucket = bucket;
it->bptr = b;
it->i = i + 1;
+ it->check_bucket = check_bucket;
return;
}
}
@@ -840,7 +928,7 @@ next:
bool
hash_gciter_init (Hmap *h, struct hash_gciter *it)
{
- // GC during map initialization
+ // GC during map initialization or on an empty map.
if(h->buckets == nil)
return false;
@@ -983,6 +1071,13 @@ next:
/// interfaces to go runtime
//
+void
+reflect·ismapkey(Type *typ, bool ret)
+{
+ ret = typ != nil && typ->alg->hash != runtime·nohash;
+ FLUSH(&ret);
+}
+
Hmap*
runtime·makemap_c(MapType *typ, int64 hint)
{
diff --git a/src/pkg/runtime/hashmap_fast.c b/src/pkg/runtime/hashmap_fast.c
index 2169f4c30..afff7b1aa 100644
--- a/src/pkg/runtime/hashmap_fast.c
+++ b/src/pkg/runtime/hashmap_fast.c
@@ -17,12 +17,14 @@ void
HASH_LOOKUP1(MapType *t, Hmap *h, KEYTYPE key, byte *value)
{
uintptr hash;
- uintptr bucket;
+ uintptr bucket, oldbucket;
Bucket *b;
- uint8 top;
uintptr i;
KEYTYPE *k;
byte *v;
+ uint8 top;
+ int8 keymaybe;
+ bool quickkey;
if(debug) {
runtime·prints("runtime.mapaccess1_fastXXX: map=");
@@ -41,23 +43,55 @@ HASH_LOOKUP1(MapType *t, Hmap *h, KEYTYPE key, byte *value)
if(docheck)
check(t, h);
- if(h->B == 0 && (h->count == 1 || QUICKEQ(key))) {
- // One-bucket table. Don't hash, just check each bucket entry.
+ if(h->B == 0) {
+ // One-bucket table. Don't hash, just check each bucket entry.
+ if(HASMAYBE) {
+ keymaybe = -1;
+ }
+ quickkey = QUICKEQ(key);
b = (Bucket*)h->buckets;
for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
- if(b->tophash[i] != 0 && EQFUNC(key, *k)) {
- value = v;
+ if(b->tophash[i] != 0) {
+ if(quickkey && EQFUNC(key, *k)) {
+ value = v;
+ FLUSH(&value);
+ return;
+ }
+ if(HASMAYBE && EQMAYBE(key, *k)) {
+ // TODO: check if key.str matches. Add EQFUNCFAST?
+ if(keymaybe >= 0) {
+ // Two same-length strings in this bucket.
+ // use slow path.
+ // TODO: keep track of more than just 1. Especially
+ // if doing the TODO above.
+ goto dohash;
+ }
+ keymaybe = i;
+ }
+ }
+ }
+ if(HASMAYBE && keymaybe >= 0) {
+ k = (KEYTYPE*)b->data + keymaybe;
+ if(EQFUNC(key, *k)) {
+ value = (byte*)((KEYTYPE*)b->data + BUCKETSIZE) + keymaybe * h->valuesize;
FLUSH(&value);
return;
}
}
} else {
+dohash:
hash = h->hash0;
HASHFUNC(&hash, sizeof(KEYTYPE), &key);
bucket = hash & (((uintptr)1 << h->B) - 1);
- if(h->oldbuckets != nil)
- grow_work(t, h, bucket);
- b = (Bucket*)(h->buckets + bucket * (offsetof(Bucket, data[0]) + BUCKETSIZE * sizeof(KEYTYPE) + BUCKETSIZE * h->valuesize));
+ if(h->oldbuckets != nil) {
+ oldbucket = bucket & (((uintptr)1 << (h->B - 1)) - 1);
+ b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
+ if(evacuated(b)) {
+ b = (Bucket*)(h->buckets + bucket * h->bucketsize);
+ }
+ } else {
+ b = (Bucket*)(h->buckets + bucket * h->bucketsize);
+ }
top = hash >> (sizeof(uintptr)*8 - 8);
if(top == 0)
top = 1;
@@ -81,12 +115,14 @@ void
HASH_LOOKUP2(MapType *t, Hmap *h, KEYTYPE key, byte *value, bool res)
{
uintptr hash;
- uintptr bucket;
+ uintptr bucket, oldbucket;
Bucket *b;
- uint8 top;
uintptr i;
KEYTYPE *k;
byte *v;
+ uint8 top;
+ int8 keymaybe;
+ bool quickkey;
if(debug) {
runtime·prints("runtime.mapaccess2_fastXXX: map=");
@@ -107,12 +143,39 @@ HASH_LOOKUP2(MapType *t, Hmap *h, KEYTYPE key, byte *value, bool res)
if(docheck)
check(t, h);
- if(h->B == 0 && (h->count == 1 || QUICKEQ(key))) {
+ if(h->B == 0) {
// One-bucket table. Don't hash, just check each bucket entry.
+ if(HASMAYBE) {
+ keymaybe = -1;
+ }
+ quickkey = QUICKEQ(key);
b = (Bucket*)h->buckets;
for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
- if(b->tophash[i] != 0 && EQFUNC(key, *k)) {
- value = v;
+ if(b->tophash[i] != 0) {
+ if(quickkey && EQFUNC(key, *k)) {
+ value = v;
+ res = true;
+ FLUSH(&value);
+ FLUSH(&res);
+ return;
+ }
+ if(HASMAYBE && EQMAYBE(key, *k)) {
+ // TODO: check if key.str matches. Add EQFUNCFAST?
+ if(keymaybe >= 0) {
+ // Two same-length strings in this bucket.
+ // use slow path.
+ // TODO: keep track of more than just 1. Especially
+ // if doing the TODO above.
+ goto dohash;
+ }
+ keymaybe = i;
+ }
+ }
+ }
+ if(HASMAYBE && keymaybe >= 0) {
+ k = (KEYTYPE*)b->data + keymaybe;
+ if(EQFUNC(key, *k)) {
+ value = (byte*)((KEYTYPE*)b->data + BUCKETSIZE) + keymaybe * h->valuesize;
res = true;
FLUSH(&value);
FLUSH(&res);
@@ -120,12 +183,19 @@ HASH_LOOKUP2(MapType *t, Hmap *h, KEYTYPE key, byte *value, bool res)
}
}
} else {
+dohash:
hash = h->hash0;
HASHFUNC(&hash, sizeof(KEYTYPE), &key);
bucket = hash & (((uintptr)1 << h->B) - 1);
- if(h->oldbuckets != nil)
- grow_work(t, h, bucket);
- b = (Bucket*)(h->buckets + bucket * (offsetof(Bucket, data[0]) + BUCKETSIZE * sizeof(KEYTYPE) + BUCKETSIZE * h->valuesize));
+ if(h->oldbuckets != nil) {
+ oldbucket = bucket & (((uintptr)1 << (h->B - 1)) - 1);
+ b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
+ if(evacuated(b)) {
+ b = (Bucket*)(h->buckets + bucket * h->bucketsize);
+ }
+ } else {
+ b = (Bucket*)(h->buckets + bucket * h->bucketsize);
+ }
top = hash >> (sizeof(uintptr)*8 - 8);
if(top == 0)
top = 1;
diff --git a/src/pkg/runtime/malloc.goc b/src/pkg/runtime/malloc.goc
index fa28e2b73..f1d25a793 100644
--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -160,7 +160,7 @@ runtime·free(void *v)
if(sizeclass == 0) {
// Large object.
size = s->npages<<PageShift;
- *(uintptr*)(s->start<<PageShift) = 1; // mark as "needs to be zeroed"
+ *(uintptr*)(s->start<<PageShift) = (uintptr)0xfeedfeedfeedfeedll; // mark as "needs to be zeroed"
// Must mark v freed before calling unmarkspan and MHeap_Free:
// they might coalesce v into other spans and change the bitmap further.
runtime·markfreed(v, size);
@@ -170,7 +170,7 @@ runtime·free(void *v)
// Small object.
size = runtime·class_to_size[sizeclass];
if(size > sizeof(uintptr))
- ((uintptr*)v)[1] = 1; // mark as "needs to be zeroed"
+ ((uintptr*)v)[1] = (uintptr)0xfeedfeedfeedfeedll; // mark as "needs to be zeroed"
// Must mark v freed before calling MCache_Free:
// it might coalesce v and other blocks into a bigger span
// and change the bitmap further.
@@ -318,7 +318,10 @@ runtime·mallocinit(void)
runtime·InitSizes();
- limit = runtime·memlimit();
+ // limit = runtime·memlimit();
+ // See https://code.google.com/p/go/issues/detail?id=5049
+ // TODO(rsc): Fix after 1.1.
+ limit = 0;
// Set up the allocation arena, a contiguous area of memory where
// allocated data will be found. The arena begins with a bitmap large
diff --git a/src/pkg/runtime/map_test.go b/src/pkg/runtime/map_test.go
index 29e19db2c..209355e93 100644
--- a/src/pkg/runtime/map_test.go
+++ b/src/pkg/runtime/map_test.go
@@ -7,8 +7,11 @@ package runtime_test
import (
"fmt"
"math"
+ "os"
"runtime"
"sort"
+ "strings"
+ "sync"
"testing"
)
@@ -231,6 +234,40 @@ func TestIterGrowWithGC(t *testing.T) {
}
}
+func TestConcurrentReadsAfterGrowth(t *testing.T) {
+ if os.Getenv("GOMAXPROCS") == "" {
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(16))
+ }
+ numLoop := 10
+ numGrowStep := 250
+ numReader := 16
+ if testing.Short() {
+ numLoop, numGrowStep = 2, 500
+ }
+ for i := 0; i < numLoop; i++ {
+ m := make(map[int]int, 0)
+ for gs := 0; gs < numGrowStep; gs++ {
+ m[gs] = gs
+ var wg sync.WaitGroup
+ wg.Add(numReader * 2)
+ for nr := 0; nr < numReader; nr++ {
+ go func() {
+ defer wg.Done()
+ for _ = range m {
+ }
+ }()
+ go func() {
+ defer wg.Done()
+ for key := 0; key < gs; key++ {
+ _ = m[key]
+ }
+ }()
+ }
+ wg.Wait()
+ }
+ }
+}
+
func TestBigItems(t *testing.T) {
var key [256]string
for i := 0; i < 256; i++ {
@@ -280,3 +317,38 @@ func TestEmptyKeyAndValue(t *testing.T) {
t.Errorf("empty key returned wrong value")
}
}
+
+// Tests a map with a single bucket, with same-lengthed short keys
+// ("quick keys") as well as long keys.
+func TestSingleBucketMapStringKeys_DupLen(t *testing.T) {
+ testMapLookups(t, map[string]string{
+ "x": "x1val",
+ "xx": "x2val",
+ "foo": "fooval",
+ "bar": "barval", // same key length as "foo"
+ "xxxx": "x4val",
+ strings.Repeat("x", 128): "longval1",
+ strings.Repeat("y", 128): "longval2",
+ })
+}
+
+// Tests a map with a single bucket, with all keys having different lengths.
+func TestSingleBucketMapStringKeys_NoDupLen(t *testing.T) {
+ testMapLookups(t, map[string]string{
+ "x": "x1val",
+ "xx": "x2val",
+ "foo": "fooval",
+ "xxxx": "x4val",
+ "xxxxx": "x5val",
+ "xxxxxx": "x6val",
+ strings.Repeat("x", 128): "longval",
+ })
+}
+
+func testMapLookups(t *testing.T, m map[string]string) {
+ for k, v := range m {
+ if m[k] != v {
+ t.Fatalf("m[%q] = %q; want %q", k, m[k], v)
+ }
+ }
+}
diff --git a/src/pkg/runtime/mapspeed_test.go b/src/pkg/runtime/mapspeed_test.go
index a37974060..3b7fbfd63 100644
--- a/src/pkg/runtime/mapspeed_test.go
+++ b/src/pkg/runtime/mapspeed_test.go
@@ -118,6 +118,17 @@ func BenchmarkMegOneMap(b *testing.B) {
}
}
+func BenchmarkMegEqMap(b *testing.B) {
+ m := make(map[string]bool)
+ key1 := strings.Repeat("X", 1<<20)
+ key2 := strings.Repeat("X", 1<<20) // equal but different instance
+ m[key1] = true
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = m[key2]
+ }
+}
+
func BenchmarkMegEmptyMap(b *testing.B) {
m := make(map[string]bool)
key := strings.Repeat("X", 1<<20)
@@ -138,6 +149,24 @@ func BenchmarkSmallStrMap(b *testing.B) {
_, _ = m[key]
}
}
+
+func BenchmarkMapStringKeysEight_16(b *testing.B) { benchmarkMapStringKeysEight(b, 16) }
+func BenchmarkMapStringKeysEight_32(b *testing.B) { benchmarkMapStringKeysEight(b, 32) }
+func BenchmarkMapStringKeysEight_64(b *testing.B) { benchmarkMapStringKeysEight(b, 64) }
+func BenchmarkMapStringKeysEight_1M(b *testing.B) { benchmarkMapStringKeysEight(b, 1<<20) }
+
+func benchmarkMapStringKeysEight(b *testing.B, keySize int) {
+ m := make(map[string]bool)
+ for i := 0; i < 8; i++ {
+ m[strings.Repeat("K", i+1)] = true
+ }
+ key := strings.Repeat("K", keySize)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _ = m[key]
+ }
+}
+
func BenchmarkIntMap(b *testing.B) {
m := make(map[int]bool)
for i := 0; i < 8; i++ {
@@ -148,3 +177,32 @@ func BenchmarkIntMap(b *testing.B) {
_, _ = m[7]
}
}
+
+// Accessing the same keys in a row.
+func benchmarkRepeatedLookup(b *testing.B, lookupKeySize int) {
+ m := make(map[string]bool)
+ // At least bigger than a single bucket:
+ for i := 0; i < 64; i++ {
+ m[fmt.Sprintf("some key %d", i)] = true
+ }
+ base := strings.Repeat("x", lookupKeySize-1)
+ key1 := base + "1"
+ key2 := base + "2"
+ b.ResetTimer()
+ for i := 0; i < b.N/4; i++ {
+ _ = m[key1]
+ _ = m[key1]
+ _ = m[key2]
+ _ = m[key2]
+ }
+}
+
+func BenchmarkRepeatedLookupStrMapKey32(b *testing.B) { benchmarkRepeatedLookup(b, 32) }
+func BenchmarkRepeatedLookupStrMapKey1M(b *testing.B) { benchmarkRepeatedLookup(b, 1<<20) }
+
+func BenchmarkNewEmptyMap(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ _ = make(map[int]int)
+ }
+}
diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c
index aa499f476..caf1b10e3 100644
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -18,6 +18,7 @@ enum {
Debug = 0,
DebugMark = 0, // run second pass to check mark
CollectStats = 0,
+ ScanStackByFrames = 0,
// Four bits per word (see #defines below).
wordsPerBitmapWord = sizeof(void*)*8/4,
@@ -1316,51 +1317,94 @@ addroot(Obj obj)
work.nroot++;
}
+// Scan a stack frame. The doframe parameter is a signal that the previously
+// scanned activation has an unknown argument size. When *doframe is true the
+// current activation must have its entire frame scanned. Otherwise, only the
+// locals need to be scanned.
+static void
+addframeroots(Func *f, byte*, byte *sp, void *doframe)
+{
+ uintptr outs;
+
+ if(thechar == '5')
+ sp += sizeof(uintptr);
+ if(f->locals == 0 || *(bool*)doframe == true)
+ addroot((Obj){sp, f->frame - sizeof(uintptr), 0});
+ else if(f->locals > 0) {
+ outs = f->frame - sizeof(uintptr) - f->locals;
+ addroot((Obj){sp + outs, f->locals, 0});
+ }
+ if(f->args > 0)
+ addroot((Obj){sp + f->frame, f->args, 0});
+ *(bool*)doframe = (f->args == ArgsSizeUnknown);
+}
+
static void
addstackroots(G *gp)
{
M *mp;
int32 n;
Stktop *stk;
- byte *sp, *guard;
+ byte *sp, *guard, *pc;
+ Func *f;
+ bool doframe;
stk = (Stktop*)gp->stackbase;
guard = (byte*)gp->stackguard;
if(gp == g) {
// Scanning our own stack: start at &gp.
- sp = (byte*)&gp;
+ sp = runtime·getcallersp(&gp);
+ pc = runtime·getcallerpc(&gp);
} else if((mp = gp->m) != nil && mp->helpgc) {
// gchelper's stack is in active use and has no interesting pointers.
return;
+ } else if(gp->gcstack != (uintptr)nil) {
+ // Scanning another goroutine that is about to enter or might
+ // have just exited a system call. It may be executing code such
+ // as schedlock and may have needed to start a new stack segment.
+ // Use the stack segment and stack pointer at the time of
+ // the system call instead, since that won't change underfoot.
+ sp = (byte*)gp->gcsp;
+ pc = gp->gcpc;
+ stk = (Stktop*)gp->gcstack;
+ guard = (byte*)gp->gcguard;
} else {
// Scanning another goroutine's stack.
// The goroutine is usually asleep (the world is stopped).
sp = (byte*)gp->sched.sp;
-
- // The exception is that if the goroutine is about to enter or might
- // have just exited a system call, it may be executing code such
- // as schedlock and may have needed to start a new stack segment.
- // Use the stack segment and stack pointer at the time of
- // the system call instead, since that won't change underfoot.
- if(gp->gcstack != (uintptr)nil) {
- stk = (Stktop*)gp->gcstack;
- sp = (byte*)gp->gcsp;
- guard = (byte*)gp->gcguard;
+ pc = gp->sched.pc;
+ if(ScanStackByFrames && pc == (byte*)runtime·goexit && gp->fnstart != nil) {
+ // The goroutine has not started. However, its incoming
+ // arguments are live at the top of the stack and must
+ // be scanned. No other live values should be on the
+ // stack.
+ f = runtime·findfunc((uintptr)gp->fnstart->fn);
+ if(f->args > 0) {
+ if(thechar == '5')
+ sp += sizeof(uintptr);
+ addroot((Obj){sp, f->args, 0});
+ }
+ return;
}
}
-
- n = 0;
- while(stk) {
- if(sp < guard-StackGuard || (byte*)stk < sp) {
- runtime·printf("scanstack inconsistent: g%D#%d sp=%p not in [%p,%p]\n", gp->goid, n, sp, guard-StackGuard, stk);
- runtime·throw("scanstack");
+ if (ScanStackByFrames) {
+ doframe = false;
+ runtime·gentraceback(pc, sp, nil, gp, 0, nil, 0x7fffffff, addframeroots, &doframe);
+ } else {
+ USED(pc);
+ n = 0;
+ while(stk) {
+ if(sp < guard-StackGuard || (byte*)stk < sp) {
+ runtime·printf("scanstack inconsistent: g%D#%d sp=%p not in [%p,%p]\n", gp->goid, n, sp, guard-StackGuard, stk);
+ runtime·throw("scanstack");
+ }
+ addroot((Obj){sp, (byte*)stk - sp, (uintptr)defaultProg | PRECISE | LOOP});
+ sp = (byte*)stk->gobuf.sp;
+ guard = stk->stackguard;
+ stk = (Stktop*)stk->stackbase;
+ n++;
}
- addroot((Obj){sp, (byte*)stk - sp, (uintptr)defaultProg | PRECISE | LOOP});
- sp = (byte*)stk->gobuf.sp;
- guard = stk->stackguard;
- stk = (Stktop*)stk->stackbase;
- n++;
}
}
@@ -1563,7 +1607,7 @@ sweepspan(ParFor *desc, uint32 idx)
if(cl == 0) {
// Free large span.
runtime·unmarkspan(p, 1<<PageShift);
- *(uintptr*)p = 1; // needs zeroing
+ *(uintptr*)p = (uintptr)0xdeaddeaddeaddeadll; // needs zeroing
runtime·MHeap_Free(runtime·mheap, s, 1);
c->local_alloc -= size;
c->local_nfree++;
@@ -1578,7 +1622,7 @@ sweepspan(ParFor *desc, uint32 idx)
break;
}
if(size > sizeof(uintptr))
- ((uintptr*)p)[1] = 1; // mark as "needs to be zeroed"
+ ((uintptr*)p)[1] = (uintptr)0xdeaddeaddeaddeadll; // mark as "needs to be zeroed"
end->next = (MLink*)p;
end = (MLink*)p;
diff --git a/src/pkg/runtime/mgc0.h b/src/pkg/runtime/mgc0.h
index 18f3654b4..d14fb37c2 100644
--- a/src/pkg/runtime/mgc0.h
+++ b/src/pkg/runtime/mgc0.h
@@ -16,6 +16,9 @@
// len Length of an array
// elemsize Size (in bytes) of an element
// size Size (in bytes)
+//
+// NOTE: There is a copy of these in ../reflect/type.go.
+// They must be kept in sync.
enum {
GC_END, // End of object, loop or subroutine. Args: none
GC_PTR, // A typed pointer. Args: (off, objgc)
diff --git a/src/pkg/runtime/mkasmh.sh b/src/pkg/runtime/mkasmh.sh
deleted file mode 100755
index f37fe2149..000000000
--- a/src/pkg/runtime/mkasmh.sh
+++ /dev/null
@@ -1,138 +0,0 @@
-#!/bin/sh
-# 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.
-
-trap "rm -f arch_GOARCH.h defs_GOOS_GOARCH.h os_GOOS.h signals_GOOS.h" EXIT INT TERM
-set -e
-
-SYS=$1
-export GOOS=$(echo $SYS | sed 's/_.*//')
-export GOARCH=$(echo $SYS | sed 's/.*_//')
-shift
-
-case "$GOARCH" in
-386) CC=8c;;
-amd64) CC=6c;;
-arm) CC=5c;;
-esac
-CC="$GOROOT/bin/tool/$CC"
-export CC
-
-export CFLAGS="-Dos_$GOOS -Darch_$GOARCH"
-
-cp arch_$GOARCH.h arch_GOARCH.h
-cp defs_${GOOS}_$GOARCH.h defs_GOOS_GOARCH.h
-cp os_$GOOS.h os_GOOS.h
-cp signals_$GOOS.h signals_GOOS.h
-
-cat <<'EOF'
-// Assembly constants.
-// AUTO-GENERATED by autogen.sh; DO NOT EDIT
-
-EOF
-if [ ! -x "$CC" ]; then
- echo "// dummy file for cmd/go to correctly generate buildscript"
- exit
-fi
-
-case "$GOARCH" in
-386)
- # The offsets 0 and 4 are also known to:
- # ../../cmd/8l/pass.c:/D_GS
- # cgo/gcc_linux_386.c:/^threadentry
- # cgo/gcc_darwin_386.c:/^threadentry
- case "$GOOS" in
- windows)
- echo '#define get_tls(r) MOVL 0x14(FS), r'
- echo '#define g(r) 0(r)'
- echo '#define m(r) 4(r)'
- ;;
- plan9)
- echo '#define get_tls(r) MOVL _tos(SB), r '
- echo '#define g(r) -8(r)'
- echo '#define m(r) -4(r)'
- ;;
- linux)
- # On Linux systems, what we call 0(GS) and 4(GS) for g and m
- # turn into %gs:-8 and %gs:-4 (using gcc syntax to denote
- # what the machine sees as opposed to 8l input).
- # 8l rewrites 0(GS) and 4(GS) into these.
- #
- # On Linux Xen, it is not allowed to use %gs:-8 and %gs:-4
- # directly. Instead, we have to store %gs:0 into a temporary
- # register and then use -8(%reg) and -4(%reg). This kind
- # of addressing is correct even when not running Xen.
- #
- # 8l can rewrite MOVL 0(GS), CX into the appropriate pair
- # of mov instructions, using CX as the intermediate register
- # (safe because CX is about to be written to anyway).
- # But 8l cannot handle other instructions, like storing into 0(GS),
- # which is where these macros come into play.
- # get_tls sets up the temporary and then g and r use it.
- #
- # The final wrinkle is that get_tls needs to read from %gs:0,
- # but in 8l input it's called 8(GS), because 8l is going to
- # subtract 8 from all the offsets, as described above.
- echo '#define get_tls(r) MOVL 8(GS), r'
- echo '#define g(r) -8(r)'
- echo '#define m(r) -4(r)'
- ;;
- *)
- echo '#define get_tls(r)'
- echo '#define g(r) 0(GS)'
- echo '#define m(r) 4(GS)'
- ;;
- esac
- ;;
-amd64)
- case "$GOOS" in
- windows)
- echo '#define get_tls(r) MOVQ 0x28(GS), r'
- echo '#define g(r) 0(r)'
- echo '#define m(r) 8(r)'
- ;;
- *)
- # The offsets 0 and 8 are known to:
- # ../../cmd/6l/pass.c:/D_GS
- # cgo/gcc_linux_amd64.c:/^threadentry
- # cgo/gcc_darwin_amd64.c:/^threadentry
- #
- echo '#define get_tls(r)'
- echo '#define g(r) 0(GS)'
- echo '#define m(r) 8(GS)'
- ;;
- esac
- ;;
-arm)
- echo '#define g R10'
- echo '#define m R9'
- echo '#define LR R14'
- ;;
-*)
- echo 'unknown $GOARCH: '$GOARCH 1>&2
- exit 1
- ;;
-esac
-echo
-
-"$CC" $CFLAGS -a proc.c |
-awk '
-{ gsub(/\r/, ""); }
-/^aggr G$/ { aggr="g" }
-/^aggr M$/ { aggr = "m" }
-/^aggr Gobuf$/ { aggr = "gobuf" }
-/^aggr WinCall$/ { aggr = "wincall" }
-/^}/ { aggr = "" }
-
-# Gobuf 24 sched;
-# 'Y' 48 stack0;
-# 'Y' 56 entry;
-# 'A' G 64 alllink;
-aggr != "" && /^ / {
- name=$NF;
- sub(/;/, "", name);
- offset=$(NF-1);
- printf("#define %s_%s %s\n", aggr, name, offset);
-}
-'
diff --git a/src/pkg/runtime/mprof.goc b/src/pkg/runtime/mprof.goc
index ebc1e3e66..707e505ba 100644
--- a/src/pkg/runtime/mprof.goc
+++ b/src/pkg/runtime/mprof.goc
@@ -511,7 +511,7 @@ saveg(byte *pc, byte *sp, G *gp, TRecord *r)
{
int32 n;
- n = runtime·gentraceback(pc, sp, 0, gp, 0, r->stk, nelem(r->stk));
+ n = runtime·gentraceback(pc, sp, 0, gp, 0, r->stk, nelem(r->stk), nil, nil);
if(n < nelem(r->stk))
r->stk[n] = 0;
}
diff --git a/src/pkg/runtime/netpoll_epoll.c b/src/pkg/runtime/netpoll_epoll.c
index d6ef0d144..9b5980700 100644
--- a/src/pkg/runtime/netpoll_epoll.c
+++ b/src/pkg/runtime/netpoll_epoll.c
@@ -57,6 +57,7 @@ runtime·netpollclose(int32 fd)
G*
runtime·netpoll(bool block)
{
+ static int32 lasterr;
EpollEvent events[128], *ev;
int32 n, i, waitms, mode;
G *gp;
@@ -69,8 +70,10 @@ runtime·netpoll(bool block)
retry:
n = runtime·epollwait(epfd, events, nelem(events), waitms);
if(n < 0) {
- if(n != -EINTR)
- runtime·printf("epollwait failed with %d\n", -n);
+ if(n != -EINTR && n != lasterr) {
+ lasterr = n;
+ runtime·printf("runtime: epollwait on fd %d failed with %d\n", epfd, -n);
+ }
goto retry;
}
gp = nil;
diff --git a/src/pkg/runtime/netpoll_kqueue.c b/src/pkg/runtime/netpoll_kqueue.c
index ad721e293..0ed03d31f 100644
--- a/src/pkg/runtime/netpoll_kqueue.c
+++ b/src/pkg/runtime/netpoll_kqueue.c
@@ -71,6 +71,7 @@ runtime·netpollclose(int32 fd)
G*
runtime·netpoll(bool block)
{
+ static int32 lasterr;
Kevent events[64], *ev;
Timespec ts, *tp;
int32 n, i;
@@ -88,8 +89,10 @@ runtime·netpoll(bool block)
retry:
n = runtime·kevent(kq, nil, 0, events, nelem(events), tp);
if(n < 0) {
- if(n != -EINTR)
- runtime·printf("kqueue failed with %d\n", -n);
+ if(n != -EINTR && n != lasterr) {
+ lasterr = n;
+ runtime·printf("runtime: kevent on fd %d failed with %d\n", kq, -n);
+ }
goto retry;
}
for(i = 0; i < n; i++) {
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 8d05730e4..eec7531e1 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -1778,7 +1778,7 @@ runtime·sigprof(uint8 *pc, uint8 *sp, uint8 *lr, G *gp)
runtime·unlock(&prof);
return;
}
- n = runtime·gentraceback(pc, sp, lr, gp, 0, prof.pcbuf, nelem(prof.pcbuf));
+ n = runtime·gentraceback(pc, sp, lr, gp, 0, prof.pcbuf, nelem(prof.pcbuf), nil, nil);
if(n > 0)
prof.fn(prof.pcbuf, n);
runtime·unlock(&prof);
diff --git a/src/pkg/runtime/race.c b/src/pkg/runtime/race.c
index cfd97041a..ce250b5b6 100644
--- a/src/pkg/runtime/race.c
+++ b/src/pkg/runtime/race.c
@@ -36,11 +36,14 @@ static bool onstack(uintptr argp);
uintptr
runtime·raceinit(void)
{
- uintptr racectx;
+ uintptr racectx, start, size;
m->racecall = true;
runtime∕race·Initialize(&racectx);
- runtime∕race·MapShadow(noptrdata, enoptrbss - noptrdata);
+ // Round data segment to page boundaries, because it's used in mmap().
+ start = (uintptr)noptrdata & ~(PageSize-1);
+ size = ROUND((uintptr)enoptrbss - start, PageSize);
+ runtime∕race·MapShadow((void*)start, size);
m->racecall = false;
return racectx;
}
diff --git a/src/pkg/runtime/race/testdata/mop_test.go b/src/pkg/runtime/race/testdata/mop_test.go
index 26cd3a4e4..fa7abe0ef 100644
--- a/src/pkg/runtime/race/testdata/mop_test.go
+++ b/src/pkg/runtime/race/testdata/mop_test.go
@@ -227,6 +227,37 @@ func TestRaceCaseFallthrough(t *testing.T) {
<-ch
}
+func TestRaceCaseType(t *testing.T) {
+ var x, y int
+ var i interface{} = x
+ c := make(chan int, 1)
+ go func() {
+ switch i.(type) {
+ case nil:
+ case int:
+ }
+ c <- 1
+ }()
+ i = y
+ <-c
+}
+
+func TestRaceCaseTypeBody(t *testing.T) {
+ var x, y int
+ var i interface{} = &x
+ c := make(chan int, 1)
+ go func() {
+ switch i := i.(type) {
+ case nil:
+ case *int:
+ *i = y
+ }
+ c <- 1
+ }()
+ x = y
+ <-c
+}
+
func TestNoRaceRange(t *testing.T) {
ch := make(chan int, 3)
a := [...]int{1, 2, 3}
@@ -339,11 +370,11 @@ func TestRaceDiv(t *testing.T) {
}
func TestRaceDivConst(t *testing.T) {
- var x, y, z int
+ var x, y, z uint32
ch := make(chan int, 2)
go func() {
- x = y / 3
+ x = y / 3 // involves only a HMUL node
ch <- 1
}()
go func() {
@@ -576,6 +607,30 @@ func TestRaceIfaceWW(t *testing.T) {
a = b
}
+func TestRaceIfaceCmp(t *testing.T) {
+ var a, b Writer
+ a = DummyWriter{1}
+ ch := make(chan bool, 1)
+ go func() {
+ a = DummyWriter{1}
+ ch <- true
+ }()
+ _ = a == b
+ <-ch
+}
+
+func TestRaceIfaceCmpNil(t *testing.T) {
+ var a Writer
+ a = DummyWriter{1}
+ ch := make(chan bool, 1)
+ go func() {
+ a = DummyWriter{1}
+ ch <- true
+ }()
+ _ = a == nil
+ <-ch
+}
+
func TestRaceEfaceConv(t *testing.T) {
c := make(chan bool)
v := 0
@@ -970,8 +1025,7 @@ func TestRaceAnd(t *testing.T) {
<-c
}
-// OANDAND is not instrumented in the compiler.
-func TestRaceFailingAnd2(t *testing.T) {
+func TestRaceAnd2(t *testing.T) {
c := make(chan bool)
x, y := 0, 0
go func() {
@@ -1007,8 +1061,7 @@ func TestRaceOr(t *testing.T) {
<-c
}
-// OOROR is not instrumented in the compiler.
-func TestRaceFailingOr2(t *testing.T) {
+func TestRaceOr2(t *testing.T) {
c := make(chan bool)
x, y := 0, 0
go func() {
@@ -1153,6 +1206,15 @@ func (p InterImpl) Foo(x int) {
_, _, _ = x, y, z
}
+type InterImpl2 InterImpl
+
+func (p *InterImpl2) Foo(x int) {
+ if p == nil {
+ InterImpl{}.Foo(x)
+ }
+ InterImpl(*p).Foo(x)
+}
+
func TestRaceInterCall(t *testing.T) {
c := make(chan bool, 1)
p := InterImpl{}
@@ -1214,6 +1276,54 @@ func TestRaceMethodCall2(t *testing.T) {
<-c
}
+// Method value with concrete value receiver.
+func TestRaceMethodValue(t *testing.T) {
+ c := make(chan bool, 1)
+ i := InterImpl{}
+ go func() {
+ i = InterImpl{}
+ c <- true
+ }()
+ _ = i.Foo
+ <-c
+}
+
+// Method value with interface receiver.
+func TestRaceMethodValue2(t *testing.T) {
+ c := make(chan bool, 1)
+ var i Inter = InterImpl{}
+ go func() {
+ i = InterImpl{}
+ c <- true
+ }()
+ _ = i.Foo
+ <-c
+}
+
+// Method value with implicit dereference.
+func TestRaceMethodValue3(t *testing.T) {
+ c := make(chan bool, 1)
+ i := &InterImpl{}
+ go func() {
+ *i = InterImpl{}
+ c <- true
+ }()
+ _ = i.Foo // dereferences i.
+ <-c
+}
+
+// Method value implicitly taking receiver address.
+func TestNoRaceMethodValue(t *testing.T) {
+ c := make(chan bool, 1)
+ i := InterImpl2{}
+ go func() {
+ i = InterImpl2{}
+ c <- true
+ }()
+ _ = i.Foo // takes the address of i only.
+ <-c
+}
+
func TestRacePanicArg(t *testing.T) {
c := make(chan bool, 1)
err := errors.New("err")
@@ -1340,6 +1450,17 @@ func TestRaceSliceSlice2(t *testing.T) {
<-c
}
+func TestRaceSliceString(t *testing.T) {
+ c := make(chan bool, 1)
+ x := "hello"
+ go func() {
+ x = "world"
+ c <- true
+ }()
+ _ = x[2:3]
+ <-c
+}
+
// http://golang.org/issue/4453
func TestRaceFailingSliceStruct(t *testing.T) {
type X struct {
@@ -1356,6 +1477,21 @@ func TestRaceFailingSliceStruct(t *testing.T) {
<-c
}
+func TestRaceFailingAppendSliceStruct(t *testing.T) {
+ type X struct {
+ x, y int
+ }
+ c := make(chan bool, 1)
+ x := make([]X, 10)
+ go func() {
+ y := make([]X, 0, 10)
+ y = append(y, x...)
+ c <- true
+ }()
+ x[1].y = 42
+ <-c
+}
+
func TestRaceStructInd(t *testing.T) {
c := make(chan bool, 1)
type Item struct {
diff --git a/src/pkg/runtime/race/testdata/slice_test.go b/src/pkg/runtime/race/testdata/slice_test.go
index 1fe051b12..c85df5e3d 100644
--- a/src/pkg/runtime/race/testdata/slice_test.go
+++ b/src/pkg/runtime/race/testdata/slice_test.go
@@ -338,8 +338,7 @@ func TestRaceSliceVarCopy2(t *testing.T) {
<-c
}
-// Not implemented.
-func TestRaceFailingSliceAppend(t *testing.T) {
+func TestRaceSliceAppend(t *testing.T) {
c := make(chan bool, 1)
s := make([]int, 10, 20)
go func() {
diff --git a/src/pkg/runtime/runtime-gdb.py b/src/pkg/runtime/runtime-gdb.py
index eff9a4003..cb70ca028 100644
--- a/src/pkg/runtime/runtime-gdb.py
+++ b/src/pkg/runtime/runtime-gdb.py
@@ -84,26 +84,35 @@ class MapTypePrinter:
return str(self.val.type)
def children(self):
- stab = self.val['st']
- i = 0
- for v in self.traverse_hash(stab):
- yield ("[%d]" % i, v['key'])
- yield ("[%d]" % (i + 1), v['val'])
- i += 2
-
- def traverse_hash(self, stab):
- ptr = stab['entry'].address
- last = stab['last']
- while ptr <= last:
- v = ptr.dereference()
- ptr = ptr + 1
- if v['hash'] == 0: continue
- if v['hash'] & 63 == 63: # subtable
- for v in self.traverse_hash(v['key'].cast(self.val['st'].type)):
- yield v
- else:
- yield v
-
+ B = self.val['b']
+ buckets = self.val['buckets']
+ oldbuckets = self.val['oldbuckets']
+ flags = self.val['flags']
+ inttype = self.val['hash0'].type
+ cnt = 0
+ for bucket in xrange(2 ** B):
+ bp = buckets + bucket
+ if oldbuckets:
+ oldbucket = bucket & (2 ** (B - 1) - 1)
+ oldbp = oldbuckets + oldbucket
+ oldb = oldbp.dereference()
+ if (oldb['overflow'].cast(inttype) & 1) == 0: # old bucket not evacuated yet
+ if bucket >= 2 ** (B - 1): continue # already did old bucket
+ bp = oldbp
+ while bp:
+ b = bp.dereference()
+ for i in xrange(8):
+ if b['tophash'][i] != 0:
+ k = b['keys'][i]
+ v = b['values'][i]
+ if flags & 1:
+ k = k.dereference()
+ if flags & 2:
+ v = v.dereference()
+ yield '%d' % cnt, k
+ yield '%d' % (cnt + 1), v
+ cnt += 2
+ bp = b['overflow']
class ChanTypePrinter:
"""Pretty print chan[T] types.
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 46c77e3fd..864b2aa5f 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -147,7 +147,12 @@ enum
// Global <-> per-M stack segment cache transfer batch size.
StackCacheBatch = 16,
};
-
+enum
+{
+ // This value is generated by the linker and should be kept in
+ // sync with cmd/ld/lib.h
+ ArgsSizeUnknown = 0x80000000,
+};
/*
* structures
*/
@@ -238,6 +243,7 @@ struct G
bool ispanic;
bool issystem; // do not output in stack dump
bool isbackground; // ignore in deadlock detector
+ bool blockingsyscall; // hint that the next syscall will block
int8 raceignore; // ignore race detection events
M* m; // for debuggers, but offset not hard-coded
M* lockedm;
@@ -587,6 +593,8 @@ void runtime·strequal(bool*, uintptr, void*, void*);
void runtime·interequal(bool*, uintptr, void*, void*);
void runtime·nilinterequal(bool*, uintptr, void*, void*);
+bool runtime·memeq(void*, void*, uintptr);
+
void runtime·memprint(uintptr, void*);
void runtime·strprint(uintptr, void*);
void runtime·interprint(uintptr, void*);
@@ -774,7 +782,7 @@ void runtime·exitsyscall(void);
G* runtime·newproc1(FuncVal*, byte*, int32, int32, void*);
bool runtime·sigsend(int32 sig);
int32 runtime·callers(int32, uintptr*, int32);
-int32 runtime·gentraceback(byte*, byte*, byte*, G*, int32, uintptr*, int32);
+int32 runtime·gentraceback(byte*, byte*, byte*, G*, int32, uintptr*, int32, void (*)(Func*, byte*, byte*, void*), void*);
int64 runtime·nanotime(void);
void runtime·dopanic(int32);
void runtime·startpanic(void);
diff --git a/src/pkg/runtime/string.goc b/src/pkg/runtime/string.goc
index c0d3f2bde..49bf1148b 100644
--- a/src/pkg/runtime/string.goc
+++ b/src/pkg/runtime/string.goc
@@ -206,8 +206,6 @@ func cmpstring(s1 String, s2 String) (v int) {
}
func eqstring(s1 String, s2 String) (v bool) {
- uintgo i, l;
-
if(s1.len != s2.len) {
v = false;
return;
@@ -216,13 +214,7 @@ func eqstring(s1 String, s2 String) (v bool) {
v = true;
return;
}
- l = s1.len;
- for(i=0; i<l; i++)
- if(s1.str[i] != s2.str[i]) {
- v = false;
- return;
- }
- v = true;
+ v = runtime·memeq(s1.str, s2.str, s1.len);
}
int32
diff --git a/src/pkg/runtime/string_test.go b/src/pkg/runtime/string_test.go
index 6ba3c1d29..df3ff06a7 100644
--- a/src/pkg/runtime/string_test.go
+++ b/src/pkg/runtime/string_test.go
@@ -47,3 +47,31 @@ func BenchmarkCompareStringDifferentLength(b *testing.B) {
}
}
}
+
+func BenchmarkCompareStringBigUnaligned(b *testing.B) {
+ bytes := make([]byte, 0, 1<<20)
+ for len(bytes) < 1<<20 {
+ bytes = append(bytes, "Hello Gophers!"...)
+ }
+ s1, s2 := string(bytes), "hello"+string(bytes)
+ for i := 0; i < b.N; i++ {
+ if s1 != s2[len("hello"):] {
+ b.Fatal("s1 != s2")
+ }
+ }
+ b.SetBytes(int64(len(s1)))
+}
+
+func BenchmarkCompareStringBig(b *testing.B) {
+ bytes := make([]byte, 0, 1<<20)
+ for len(bytes) < 1<<20 {
+ bytes = append(bytes, "Hello Gophers!"...)
+ }
+ s1, s2 := string(bytes), string(bytes)
+ for i := 0; i < b.N; i++ {
+ if s1 != s2 {
+ b.Fatal("s1 != s2")
+ }
+ }
+ b.SetBytes(int64(len(s1)))
+}
diff --git a/src/pkg/runtime/traceback_arm.c b/src/pkg/runtime/traceback_arm.c
index dd85cc02c..ae2d3241f 100644
--- a/src/pkg/runtime/traceback_arm.c
+++ b/src/pkg/runtime/traceback_arm.c
@@ -17,9 +17,9 @@ void _divu(void);
void _modu(void);
int32
-runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max)
+runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max, void (*fn)(Func*, byte*, byte*, void*), void *arg)
{
- int32 i, n, iter;
+ int32 i, n;
uintptr pc, lr, tracepc, x;
byte *fp;
bool waspanic;
@@ -46,7 +46,7 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
n = 0;
stk = (Stktop*)gp->stackbase;
- for(iter = 0; iter < 100 && n < max; iter++) { // iter avoids looping forever
+ while(n < max) {
// Typically:
// pc is the PC of the running function.
// sp is the stack pointer at that program counter.
@@ -60,28 +60,34 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
sp = (byte*)stk->gobuf.sp;
lr = 0;
fp = nil;
- if(pcbuf == nil && runtime·showframe(nil, gp == m->curg))
+ if(pcbuf == nil && fn == nil && runtime·showframe(nil, gp == m->curg))
runtime·printf("----- stack segment boundary -----\n");
stk = (Stktop*)stk->stackbase;
continue;
}
- if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil)
+ if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil) {
+ if(fn != nil)
+ runtime·throw("unknown pc");
break;
+ }
// Found an actual function.
if(lr == 0)
lr = *(uintptr*)sp;
if(fp == nil) {
fp = sp;
- if(pc > f->entry && f->frame >= 0)
- fp += f->frame;
+ if(pc > f->entry && f->frame >= sizeof(uintptr))
+ fp += f->frame - sizeof(uintptr);
+ fp += sizeof(uintptr);
}
if(skip > 0)
skip--;
else if(pcbuf != nil)
pcbuf[n++] = pc;
+ else if(fn != nil)
+ (*fn)(f, (byte*)pc, sp, arg);
else {
if(runtime·showframe(f, gp == m->curg)) {
// Print during crash.
@@ -113,7 +119,7 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
waspanic = f->entry == (uintptr)runtime·sigpanic;
- if(pcbuf == nil && f->entry == (uintptr)runtime·newstack && gp == m->g0) {
+ if(pcbuf == nil && fn == nil && f->entry == (uintptr)runtime·newstack && gp == m->g0) {
runtime·printf("----- newstack called from goroutine %D -----\n", m->curg->goid);
pc = (uintptr)m->morepc;
sp = (byte*)m->moreargp - sizeof(void*);
@@ -124,7 +130,7 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
continue;
}
- if(pcbuf == nil && f->entry == (uintptr)runtime·lessstack && gp == m->g0) {
+ if(pcbuf == nil && fn == nil && f->entry == (uintptr)runtime·lessstack && gp == m->g0) {
runtime·printf("----- lessstack called from goroutine %D -----\n", m->curg->goid);
gp = m->curg;
stk = (Stktop*)gp->stackbase;
@@ -135,6 +141,10 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
continue;
}
+ // Do not unwind past the bottom of the stack.
+ if(pc == (uintptr)runtime·goexit)
+ break;
+
// Unwind to next frame.
pc = lr;
lr = 0;
@@ -162,7 +172,7 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
}
}
- if(pcbuf == nil && (pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil
+ if(pcbuf == nil && fn == nil && (pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil
&& runtime·showframe(f, gp == m->curg) && gp->goid != 1) {
runtime·printf("created by %S\n", f->name);
tracepc = pc; // back up to CALL instruction for funcline.
@@ -186,7 +196,7 @@ runtime·traceback(byte *pc0, byte *sp, byte *lr, G *gp)
sp = (byte*)gp->sched.sp;
lr = nil;
}
- runtime·gentraceback(pc0, sp, lr, gp, 0, nil, 100);
+ runtime·gentraceback(pc0, sp, lr, gp, 0, nil, 100, nil, nil);
}
// func caller(n int) (pc uintptr, file string, line int, ok bool)
@@ -198,5 +208,5 @@ runtime·callers(int32 skip, uintptr *pcbuf, int32 m)
sp = runtime·getcallersp(&skip);
pc = runtime·getcallerpc(&skip);
- return runtime·gentraceback(pc, sp, 0, g, skip, pcbuf, m);
+ return runtime·gentraceback(pc, sp, 0, g, skip, pcbuf, m, nil, nil);
}
diff --git a/src/pkg/runtime/traceback_x86.c b/src/pkg/runtime/traceback_x86.c
index 72603ae8e..ce52df870 100644
--- a/src/pkg/runtime/traceback_x86.c
+++ b/src/pkg/runtime/traceback_x86.c
@@ -17,14 +17,14 @@ void runtime·sigpanic(void);
// This code is also used for the 386 tracebacks.
// Use uintptr for an appropriate word-sized integer.
-// Generic traceback. Handles runtime stack prints (pcbuf == nil)
-// as well as the runtime.Callers function (pcbuf != nil).
-// A little clunky to merge the two but avoids duplicating
-// the code and all its subtlety.
+// Generic traceback. Handles runtime stack prints (pcbuf == nil),
+// the runtime.Callers function (pcbuf != nil), as well as the garbage
+// collector (fn != nil). A little clunky to merge the two but avoids
+// duplicating the code and all its subtlety.
int32
-runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max)
+runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max, void (*fn)(Func*, byte*, byte*, void*), void *arg)
{
- int32 i, n, iter, sawnewstack;
+ int32 i, n, sawnewstack;
uintptr pc, lr, tracepc;
byte *fp;
Stktop *stk;
@@ -54,7 +54,7 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
n = 0;
sawnewstack = 0;
stk = (Stktop*)gp->stackbase;
- for(iter = 0; iter < 100 && n < max; iter++) { // iter avoids looping forever
+ while(n < max) {
// Typically:
// pc is the PC of the running function.
// sp is the stack pointer at that program counter.
@@ -68,13 +68,16 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
sp = (byte*)stk->gobuf.sp;
lr = 0;
fp = nil;
- if(pcbuf == nil && runtime·showframe(nil, gp == m->curg))
+ if(pcbuf == nil && fn == nil && runtime·showframe(nil, gp == m->curg))
runtime·printf("----- stack segment boundary -----\n");
stk = (Stktop*)stk->stackbase;
continue;
}
- if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil)
+ if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil) {
+ if(fn != nil)
+ runtime·throw("unknown pc");
break;
+ }
// Found an actual function.
if(fp == nil) {
@@ -91,6 +94,8 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
skip--;
else if(pcbuf != nil)
pcbuf[n++] = pc;
+ else if(fn != nil)
+ (*fn)(f, (byte*)pc, sp, arg);
else {
if(runtime·showframe(f, gp == m->curg)) {
// Print during crash.
@@ -129,7 +134,7 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
if(f->entry == (uintptr)runtime·newstack)
sawnewstack = 1;
- if(pcbuf == nil && f->entry == (uintptr)runtime·morestack && gp == m->g0 && sawnewstack) {
+ if(pcbuf == nil && fn == nil && f->entry == (uintptr)runtime·morestack && gp == m->g0 && sawnewstack) {
// The fact that we saw newstack means that morestack
// has managed to record its information in m, so we can
// use it to keep unwinding the stack.
@@ -144,7 +149,7 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
continue;
}
- if(pcbuf == nil && f->entry == (uintptr)runtime·lessstack && gp == m->g0) {
+ if(pcbuf == nil && fn == nil && f->entry == (uintptr)runtime·lessstack && gp == m->g0) {
// Lessstack is running on scheduler stack. Switch to original goroutine.
runtime·printf("----- lessstack called from goroutine %D -----\n", m->curg->goid);
gp = m->curg;
@@ -156,6 +161,10 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
continue;
}
+ // Do not unwind past the bottom of the stack.
+ if(pc == (uintptr)runtime·goexit)
+ break;
+
// Unwind to next frame.
pc = lr;
lr = 0;
@@ -164,7 +173,7 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
}
// Show what created goroutine, except main goroutine (goid 1).
- if(pcbuf == nil && (pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil
+ if(pcbuf == nil && fn == nil && (pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil
&& runtime·showframe(f, gp == m->curg) && gp->goid != 1) {
runtime·printf("created by %S\n", f->name);
tracepc = pc; // back up to CALL instruction for funcline.
@@ -187,7 +196,7 @@ runtime·traceback(byte *pc0, byte *sp, byte*, G *gp)
pc0 = gp->sched.pc;
sp = (byte*)gp->sched.sp;
}
- runtime·gentraceback(pc0, sp, nil, gp, 0, nil, 100);
+ runtime·gentraceback(pc0, sp, nil, gp, 0, nil, 100, nil, nil);
}
int32
@@ -199,5 +208,5 @@ runtime·callers(int32 skip, uintptr *pcbuf, int32 m)
sp = (byte*)&skip;
pc = runtime·getcallerpc(&skip);
- return runtime·gentraceback(pc, sp, nil, g, skip, pcbuf, m);
+ return runtime·gentraceback(pc, sp, nil, g, skip, pcbuf, m, nil, nil);
}
diff --git a/src/pkg/sort/example_multi_test.go b/src/pkg/sort/example_multi_test.go
new file mode 100644
index 000000000..d0a9e7dc3
--- /dev/null
+++ b/src/pkg/sort/example_multi_test.go
@@ -0,0 +1,132 @@
+// 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 sort_test
+
+import (
+ "fmt"
+ "sort"
+)
+
+// A Change is a record of source code changes, recording user, language, and delta size.
+type Change struct {
+ user string
+ language string
+ lines int
+}
+
+type lessFunc func(p1, p2 *Change) bool
+
+// multiSorter implements the Sort interface, sorting the changes within.
+type multiSorter struct {
+ changes []Change
+ less []lessFunc
+}
+
+// Sort sorts the argument slice according to the less functions passed to OrderedBy.
+func (ms *multiSorter) Sort(changes []Change) {
+ sort.Sort(ms)
+}
+
+// OrderedBy returns a Sorter that sorts using the less functions, in order.
+// Call its Sort method to sort the data.
+func OrderedBy(less ...lessFunc) *multiSorter {
+ return &multiSorter{
+ changes: changes,
+ less: less,
+ }
+}
+
+// Len is part of sort.Interface.
+func (ms *multiSorter) Len() int {
+ return len(ms.changes)
+}
+
+// Swap is part of sort.Interface.
+func (ms *multiSorter) Swap(i, j int) {
+ ms.changes[i], ms.changes[j] = ms.changes[j], ms.changes[i]
+}
+
+// Less is part of sort.Interface. It is implemented by looping along the
+// less functions until it finds a comparison that is either Less or
+// !Less. Note that it can call the less functions twice per call. We
+// could change the functions to return -1, 0, 1 and reduce the
+// number of calls for greater efficiency: an exercise for the reader.
+func (ms *multiSorter) Less(i, j int) bool {
+ p, q := &ms.changes[i], &ms.changes[j]
+ // Try all but the last comparison.
+ var k int
+ for k = 0; k < len(ms.less)-1; k++ {
+ less := ms.less[k]
+ switch {
+ case less(p, q):
+ // p < q, so we have a decision.
+ return true
+ case less(q, p):
+ // p > q, so we have a decision.
+ return false
+ }
+ // p == q; try the next comparison.
+ }
+ // All comparisons to here said "equal", so just return whatever
+ // the final comparison reports.
+ return ms.less[k](p, q)
+}
+
+var changes = []Change{
+ {"gri", "Go", 100},
+ {"ken", "C", 150},
+ {"glenda", "Go", 200},
+ {"rsc", "Go", 200},
+ {"r", "Go", 100},
+ {"ken", "Go", 200},
+ {"dmr", "C", 100},
+ {"r", "C", 150},
+ {"gri", "Smalltalk", 80},
+}
+
+// ExampleMultiKeys demonstrates a technique for sorting a struct type using different
+// sets of multiple fields in the comparison. We chain together "Less" functions, each of
+// which compares a single field.
+func Example_sortMultiKeys() {
+ // Closures that order the Change structure.
+ user := func(c1, c2 *Change) bool {
+ return c1.user < c2.user
+ }
+ language := func(c1, c2 *Change) bool {
+ return c1.language < c2.language
+ }
+ increasingLines := func(c1, c2 *Change) bool {
+ return c1.lines < c2.lines
+ }
+ decreasingLines := func(c1, c2 *Change) bool {
+ return c1.lines > c2.lines // Note: > orders downwards.
+ }
+
+ // Simple use: Sort by user.
+ OrderedBy(user).Sort(changes)
+ fmt.Println("By user:", changes)
+
+ // multiSorter implements the Sort interface, so we can also do this.
+ sort.Sort(OrderedBy(user, increasingLines))
+ fmt.Println("By user,<lines:", changes)
+
+ // More examples.
+ OrderedBy(user, decreasingLines).Sort(changes)
+ fmt.Println("By user,>lines:", changes)
+
+ OrderedBy(language, increasingLines).Sort(changes)
+ fmt.Println("By language,<lines:", changes)
+
+ OrderedBy(language, increasingLines, user).Sort(changes)
+ fmt.Println("By language,<lines,user:", changes)
+
+ // Output:
+ //By user: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken Go 200} {ken C 150} {r Go 100} {r C 150} {rsc Go 200}]
+ //By user,<lines: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}]
+ //By user,>lines: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken Go 200} {ken C 150} {r C 150} {r Go 100} {rsc Go 200}]
+ //By language,<lines: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {ken Go 200} {glenda Go 200} {rsc Go 200} {gri Smalltalk 80}]
+ //By language,<lines,user: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {glenda Go 200} {ken Go 200} {rsc Go 200} {gri Smalltalk 80}]
+
+}
diff --git a/src/pkg/strconv/strconv_test.go b/src/pkg/strconv/strconv_test.go
index 3cd7835cc..40ab4ce6a 100644
--- a/src/pkg/strconv/strconv_test.go
+++ b/src/pkg/strconv/strconv_test.go
@@ -20,14 +20,12 @@ var (
desc string
fn func()
}{
- // TODO(bradfitz): this might be 0, once escape analysis is better
- {1, `AppendInt(localBuf[:0], 123, 10)`, func() {
+ {0, `AppendInt(localBuf[:0], 123, 10)`, func() {
var localBuf [64]byte
AppendInt(localBuf[:0], 123, 10)
}},
{0, `AppendInt(globalBuf[:0], 123, 10)`, func() { AppendInt(globalBuf[:0], 123, 10) }},
- // TODO(bradfitz): this might be 0, once escape analysis is better
- {1, `AppendFloat(localBuf[:0], 1.23, 'g', 5, 64)`, func() {
+ {0, `AppendFloat(localBuf[:0], 1.23, 'g', 5, 64)`, func() {
var localBuf [64]byte
AppendFloat(localBuf[:0], 1.23, 'g', 5, 64)
}},
diff --git a/src/pkg/sync/atomic/asm_arm.s b/src/pkg/sync/atomic/asm_arm.s
index 4faf5b5d9..a0525881e 100644
--- a/src/pkg/sync/atomic/asm_arm.s
+++ b/src/pkg/sync/atomic/asm_arm.s
@@ -29,6 +29,10 @@ casfail:
TEXT ·armCompareAndSwapUint64(SB),7,$0
BL fastCheck64<>(SB)
MOVW addr+0(FP), R1
+ // make unaligned atomic access panic
+ AND.S $7, R1, R2
+ BEQ 2(PC)
+ MOVW R2, (R2)
MOVW oldlo+4(FP), R2
MOVW oldhi+8(FP), R3
MOVW newlo+12(FP), R4
@@ -67,6 +71,10 @@ addloop:
TEXT ·armAddUint64(SB),7,$0
BL fastCheck64<>(SB)
MOVW addr+0(FP), R1
+ // make unaligned atomic access panic
+ AND.S $7, R1, R2
+ BEQ 2(PC)
+ MOVW R2, (R2)
MOVW deltalo+4(FP), R2
MOVW deltahi+8(FP), R3
add64loop:
@@ -84,6 +92,10 @@ add64loop:
TEXT ·armLoadUint64(SB),7,$0
BL fastCheck64<>(SB)
MOVW addr+0(FP), R1
+ // make unaligned atomic access panic
+ AND.S $7, R1, R2
+ BEQ 2(PC)
+ MOVW R2, (R2)
load64loop:
LDREXD (R1), R2 // loads R2 and R3
STREXD R2, (R1), R0 // stores R2 and R3
@@ -96,6 +108,10 @@ load64loop:
TEXT ·armStoreUint64(SB),7,$0
BL fastCheck64<>(SB)
MOVW addr+0(FP), R1
+ // make unaligned atomic access panic
+ AND.S $7, R1, R2
+ BEQ 2(PC)
+ MOVW R2, (R2)
MOVW vallo+4(FP), R2
MOVW valhi+8(FP), R3
store64loop:
diff --git a/src/pkg/sync/atomic/asm_linux_arm.s b/src/pkg/sync/atomic/asm_linux_arm.s
index 098acf35b..5b16894b9 100644
--- a/src/pkg/sync/atomic/asm_linux_arm.s
+++ b/src/pkg/sync/atomic/asm_linux_arm.s
@@ -80,6 +80,10 @@ TEXT cas64<>(SB),7,$0
TEXT kernelCAS64<>(SB),7,$0
// int (*__kuser_cmpxchg64_t)(const int64_t *oldval, const int64_t *newval, volatile int64_t *ptr);
MOVW addr+0(FP), R2 // ptr
+ // make unaligned atomic access panic
+ AND.S $7, R2, R1
+ BEQ 2(PC)
+ MOVW R1, (R1)
MOVW $4(FP), R0 // oldval
MOVW $12(FP), R1 // newval
BL cas64<>(SB)
@@ -91,6 +95,10 @@ TEXT kernelCAS64<>(SB),7,$0
TEXT generalCAS64<>(SB),7,$20
// bool runtime·cas64(uint64 volatile *addr, uint64 *old, uint64 new)
MOVW addr+0(FP), R0
+ // make unaligned atomic access panic
+ AND.S $7, R0, R1
+ BEQ 2(PC)
+ MOVW R1, (R1)
MOVW R0, 4(R13)
MOVW $4(FP), R1 // oldval
MOVW R1, 8(R13)
diff --git a/src/pkg/sync/atomic/atomic_test.go b/src/pkg/sync/atomic/atomic_test.go
index 72f303040..b392df595 100644
--- a/src/pkg/sync/atomic/atomic_test.go
+++ b/src/pkg/sync/atomic/atomic_test.go
@@ -1189,10 +1189,10 @@ func shouldPanic(t *testing.T, name string, f func()) {
func TestUnaligned64(t *testing.T) {
// Unaligned 64-bit atomics on 32-bit systems are
- // a continual source of pain. Test that on 386 they crash
+ // a continual source of pain. Test that on 32-bit systems they crash
// instead of failing silently.
- if runtime.GOARCH != "386" {
- t.Skip("test only runs on 386")
+ if unsafe.Sizeof(int(0)) != 4 {
+ t.Skip("test only runs on 32-bit systems")
}
x := make([]uint32, 4)
diff --git a/src/pkg/syscall/passfd_test.go b/src/pkg/syscall/passfd_test.go
index a0e590950..e16debae1 100644
--- a/src/pkg/syscall/passfd_test.go
+++ b/src/pkg/syscall/passfd_test.go
@@ -49,7 +49,7 @@ func TestPassFD(t *testing.T) {
defer readFile.Close()
cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir)
- cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...)
+ cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
cmd.ExtraFiles = []*os.File{writeFile}
out, err := cmd.CombinedOutput()
diff --git a/src/pkg/testing/testing.go b/src/pkg/testing/testing.go
index 86cd46c29..312d28732 100644
--- a/src/pkg/testing/testing.go
+++ b/src/pkg/testing/testing.go
@@ -18,6 +18,8 @@
// ...
// }
//
+// Benchmarks
+//
// Functions of the form
// func BenchmarkXxx(*testing.B)
// are considered benchmarks, and are executed by the "go test" command when
@@ -49,6 +51,8 @@
// }
// }
//
+// Examples
+//
// The package also runs and verifies example code. Example functions may
// include a concluding line comment that begins with "Output:" and is compared with
// the standard output of the function when the tests are run. (The comparison
@@ -246,11 +250,13 @@ func (c *common) log(s string) {
}
// Log formats its arguments using default formatting, analogous to Println,
-// and records the text in the error log.
+// and records the text in the error log. The text will be printed only if
+// the test fails or the -test.v flag is set.
func (c *common) Log(args ...interface{}) { c.log(fmt.Sprintln(args...)) }
// Logf formats its arguments according to the format, analogous to Printf,
-// and records the text in the error log.
+// and records the text in the error log. The text will be printed only if
+// the test fails or the -test.v flag is set.
func (c *common) Logf(format string, args ...interface{}) { c.log(fmt.Sprintf(format, args...)) }
// Error is equivalent to Log followed by Fail.
@@ -337,6 +343,7 @@ func tRunner(t *T, test *InternalTest) {
t.duration = time.Now().Sub(t.start)
// If the test panicked, print any test output before dying.
if err := recover(); err != nil {
+ t.Fail()
t.report()
panic(err)
}
diff --git a/src/pkg/text/template/exec.go b/src/pkg/text/template/exec.go
index 12c40b70f..8ec8174a1 100644
--- a/src/pkg/text/template/exec.go
+++ b/src/pkg/text/template/exec.go
@@ -619,6 +619,8 @@ func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) refle
return s.validateType(s.evalVariableNode(dot, arg, nil, zero), typ)
case *parse.PipeNode:
return s.validateType(s.evalPipeline(dot, arg), typ)
+ case *parse.IdentifierNode:
+ return s.evalFunction(dot, arg, arg, nil, zero)
}
switch typ.Kind() {
case reflect.Bool:
diff --git a/src/pkg/text/template/exec_test.go b/src/pkg/text/template/exec_test.go
index 0f8beec5e..0ab20acc9 100644
--- a/src/pkg/text/template/exec_test.go
+++ b/src/pkg/text/template/exec_test.go
@@ -499,6 +499,8 @@ var execTests = []execTest{
{"bug8b", "{{4|dddArg 3}}", "", tVal, false},
// A bug was introduced that broke map lookups for lower-case names.
{"bug9", "{{.cause}}", "neglect", map[string]string{"cause": "neglect"}, true},
+ // Field chain starting with function did not work.
+ {"bug10", "{{mapOfThree.three}}-{{(mapOfThree).three}}", "3-3", 0, true},
}
func zeroArgs() string {
@@ -560,19 +562,24 @@ func stringer(s fmt.Stringer) string {
return s.String()
}
+func mapOfThree() interface{} {
+ return map[string]int{"three": 3}
+}
+
func testExecute(execTests []execTest, template *Template, t *testing.T) {
b := new(bytes.Buffer)
funcs := FuncMap{
- "add": add,
- "count": count,
- "dddArg": dddArg,
- "echo": echo,
- "makemap": makemap,
- "oneArg": oneArg,
- "typeOf": typeOf,
- "vfunc": vfunc,
- "zeroArgs": zeroArgs,
- "stringer": stringer,
+ "add": add,
+ "count": count,
+ "dddArg": dddArg,
+ "echo": echo,
+ "makemap": makemap,
+ "mapOfThree": mapOfThree,
+ "oneArg": oneArg,
+ "stringer": stringer,
+ "typeOf": typeOf,
+ "vfunc": vfunc,
+ "zeroArgs": zeroArgs,
}
for _, test := range execTests {
var tmpl *Template
diff --git a/src/run.bash b/src/run.bash
index 538d4f6fe..b197844fe 100755
--- a/src/run.bash
+++ b/src/run.bash
@@ -35,16 +35,20 @@ fi
# at least runtime/debug test will fail.
unset GOROOT_FINAL
+# increase timeout for ARM up to 3 times the normal value
+timeout_scale=1
+[ "$GOARCH" == "arm" ] && timeout_scale=3
+
echo '# Testing packages.'
-time go test std -short -timeout=120s
+time go test std -short -timeout=$(expr 120 \* $timeout_scale)s
echo
echo '# GOMAXPROCS=2 runtime -cpu=1,2,4'
-GOMAXPROCS=2 go test runtime -short -timeout=240s -cpu=1,2,4
+GOMAXPROCS=2 go test runtime -short -timeout=$(expr 240 \* $timeout_scale)s -cpu=1,2,4
echo
echo '# sync -cpu=10'
-go test sync -short -timeout=120s -cpu=10
+go test sync -short -timeout=$(expr 120 \* $timeout_scale)s -cpu=10
# Race detector only supported on Linux and OS X,
# and only on amd64, and only when cgo is enabled.
@@ -75,11 +79,26 @@ go run $GOROOT/test/run.go - .
[ "$CGO_ENABLED" != 1 ] ||
(xcd ../misc/cgo/test
+set -e
go test -ldflags '-linkmode=auto'
go test -ldflags '-linkmode=internal'
case "$GOHOSTOS-$GOARCH" in
-darwin-386 | darwin-amd64 | freebsd-386 | freebsd-amd64 | linux-386 | linux-amd64 | netbsd-386 | netbsd-amd64 | openbsd-386 | openbsd-amd64)
+openbsd-386 | openbsd-amd64)
+ # test linkmode=external, but __thread not supported, so skip testtls.
+ go test -ldflags '-linkmode=external'
+ ;;
+darwin-386 | darwin-amd64)
+ # linkmode=external fails on OS X 10.6 and earlier == Darwin
+ # 10.8 and earlier.
+ case $(uname -r) in
+ [0-9].* | 10.*) ;;
+ *) go test -ldflags '-linkmode=external' ;;
+ esac
+ ;;
+freebsd-386 | freebsd-amd64 | linux-386 | linux-amd64 | netbsd-386 | netbsd-amd64)
go test -ldflags '-linkmode=external'
+ go test -ldflags '-linkmode=auto' ../testtls
+ go test -ldflags '-linkmode=external' ../testtls
esac
) || exit $?
diff --git a/src/run.bat b/src/run.bat
index 3f950f541..1a68eb573 100644
--- a/src/run.bat
+++ b/src/run.bat
@@ -74,12 +74,12 @@ echo.
:: cgo tests
if x%CGO_ENABLED% == x0 goto nocgo
echo # ..\misc\cgo\life
-go run %GOROOT%\test\run.go - ..\misc\cgo\life
+go run "%GOROOT%\test\run.go" - ..\misc\cgo\life
if errorlevel 1 goto fail
echo.
echo # ..\misc\cgo\stdio
-go run %GOROOT%\test\run.go - ..\misc\cgo\stdio
+go run "%GOROOT%\test\run.go" - ..\misc\cgo\stdio
if errorlevel 1 goto fail
echo.
@@ -90,7 +90,7 @@ echo.
:nocgo
echo # ..\doc\progs
-go run %GOROOT%\test\run.go - ..\doc\progs
+go run "%GOROOT%\test\run.go" - ..\doc\progs
if errorlevel 1 goto fail
echo.