summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2014-10-16 15:00:08 -0400
committerRuss Cox <rsc@golang.org>2014-10-16 15:00:08 -0400
commit78db2eb9c66d478bee0d56edcc55d4f865f8c6c7 (patch)
treed08da7ddd82570422ae87dc9d3ac061f8ce3b648 /src
parentaf64ab79c0ad72decf083d78cf54257d009741b5 (diff)
parent25c63c2c30f4807b073f849d248144d97893ce70 (diff)
downloadgo-78db2eb9c66d478bee0d56edcc55d4f865f8c6c7.tar.gz
all: merge default branch into dev.garbage
hg was unable to create a CL on the code review server for this, so I am submitting the merge by hand. The only manual edits are in mgc0.c, to reapply the removal of cached/ncached to the new code.
Diffstat (limited to 'src')
-rw-r--r--src/cmd/5a/lex.c1
-rw-r--r--src/cmd/5c/reg.c9
-rw-r--r--src/cmd/6a/lex.c1
-rw-r--r--src/cmd/6c/reg.c14
-rw-r--r--src/cmd/8a/lex.c1
-rw-r--r--src/cmd/8c/reg.c9
-rw-r--r--src/cmd/cc/godefs.c6
-rw-r--r--src/cmd/gc/builtin.c5
-rw-r--r--src/cmd/gc/dcl.c14
-rw-r--r--src/cmd/gc/order.c6
-rw-r--r--src/cmd/gc/pgen.c2
-rw-r--r--src/cmd/gc/racewalk.c7
-rw-r--r--src/cmd/gc/reflect.c25
-rw-r--r--src/cmd/gc/runtime.go7
-rw-r--r--src/cmd/gc/sinit.c2
-rw-r--r--src/cmd/gc/walk.c32
-rw-r--r--src/cmd/go/build.go2
-rw-r--r--src/cmd/go/doc.go39
-rw-r--r--src/cmd/go/list.go39
-rw-r--r--src/cmd/go/pkg.go22
-rwxr-xr-xsrc/cmd/go/test.bash14
-rw-r--r--src/cmd/go/testdata/src/vetpkg/a_test.go1
-rw-r--r--src/cmd/go/testdata/src/vetpkg/b.go7
-rw-r--r--src/cmd/go/vet.go23
-rw-r--r--src/cmd/ld/decodesym.c5
-rw-r--r--src/cmd/ld/dwarf.c14
-rw-r--r--src/cmd/ld/ldelf.c2
-rw-r--r--src/cmd/ld/ldpe.c26
-rw-r--r--src/cmd/ld/macho.c3
-rw-r--r--src/crypto/x509/x509.go17
-rw-r--r--src/crypto/x509/x509_test.go63
-rw-r--r--src/database/sql/fakedb_test.go22
-rw-r--r--src/database/sql/sql.go11
-rw-r--r--src/debug/elf/file.go8
-rw-r--r--src/encoding/asn1/asn1.go4
-rw-r--r--src/encoding/asn1/asn1_test.go48
-rw-r--r--src/encoding/gob/encode.go10
-rw-r--r--src/encoding/json/decode.go40
-rw-r--r--src/encoding/json/decode_test.go21
-rw-r--r--src/encoding/json/encode.go2
-rw-r--r--src/go/build/build.go27
-rw-r--r--src/go/build/build_test.go18
-rw-r--r--src/go/build/doc.go10
-rw-r--r--src/go/build/testdata/empty/dummy0
-rw-r--r--src/go/build/testdata/multi/file.go5
-rw-r--r--src/go/build/testdata/multi/file_appengine.go5
-rw-r--r--src/liblink/data.c2
-rw-r--r--src/math/big/int.go19
-rw-r--r--src/math/big/int_test.go36
-rw-r--r--src/math/big/rat.go7
-rw-r--r--src/math/big/rat_test.go1
-rw-r--r--src/net/fd_windows.go12
-rw-r--r--src/net/http/client.go28
-rw-r--r--src/net/http/client_test.go37
-rw-r--r--src/net/http/export_test.go5
-rw-r--r--src/net/http/response_test.go28
-rw-r--r--src/net/http/serve_test.go97
-rw-r--r--src/net/http/server.go32
-rw-r--r--src/net/http/transport.go11
-rw-r--r--src/net/http/transport_test.go33
-rw-r--r--src/net/rpc/client.go12
-rw-r--r--src/net/rpc/client_test.go55
-rw-r--r--src/net/udp_test.go41
-rw-r--r--src/net/url/url.go18
-rw-r--r--src/os/error_plan9.go3
-rw-r--r--src/os/exec/exec.go11
-rw-r--r--src/os/exec_unix.go14
-rw-r--r--src/os/exec_windows.go3
-rw-r--r--src/os/os_test.go47
-rw-r--r--src/os/path.go6
-rw-r--r--src/reflect/all_test.go83
-rw-r--r--src/reflect/makefunc.go6
-rw-r--r--src/reflect/type.go62
-rw-r--r--src/reflect/value.go368
-rw-r--r--src/regexp/syntax/doc.go48
-rw-r--r--src/runtime/asm_386.s75
-rw-r--r--src/runtime/asm_amd64.s75
-rw-r--r--src/runtime/asm_amd64p32.s80
-rw-r--r--src/runtime/asm_arm.s73
-rw-r--r--src/runtime/cgocallback.go3
-rw-r--r--src/runtime/chan.go11
-rw-r--r--src/runtime/defs_windows.go4
-rw-r--r--src/runtime/defs_windows_386.h4
-rw-r--r--src/runtime/defs_windows_amd64.h4
-rw-r--r--src/runtime/heapdump.c44
-rw-r--r--src/runtime/malloc.go2
-rw-r--r--src/runtime/mgc0.c40
-rw-r--r--src/runtime/mgc0.go21
-rw-r--r--src/runtime/mgc0.h2
-rw-r--r--src/runtime/os_windows.c52
-rw-r--r--src/runtime/os_windows_386.c84
-rw-r--r--src/runtime/os_windows_amd64.c97
-rw-r--r--src/runtime/panic.c4
-rw-r--r--src/runtime/panic.go27
-rw-r--r--src/runtime/runtime.h5
-rw-r--r--src/runtime/stack.c39
-rw-r--r--src/runtime/stubs.go30
-rw-r--r--src/runtime/sys_windows_386.s18
-rw-r--r--src/runtime/sys_windows_amd64.s18
-rw-r--r--src/runtime/syscall_windows_test.go39
-rw-r--r--src/runtime/type.h2
-rw-r--r--src/strings/strings.go9
-rw-r--r--src/syscall/syscall_windows.go4
-rw-r--r--src/syscall/ztypes_windows.go1
104 files changed, 1810 insertions, 831 deletions
diff --git a/src/cmd/5a/lex.c b/src/cmd/5a/lex.c
index 84a17d155..9c6970947 100644
--- a/src/cmd/5a/lex.c
+++ b/src/cmd/5a/lex.c
@@ -85,6 +85,7 @@ main(int argc, char *argv[])
ctxt = linknew(&linkarm);
ctxt->diag = yyerror;
ctxt->bso = &bstdout;
+ ctxt->enforce_data_order = 1;
Binit(&bstdout, 1, OWRITE);
listinit5();
fmtinstall('L', Lconv);
diff --git a/src/cmd/5c/reg.c b/src/cmd/5c/reg.c
index 2fbe031f4..9024d5f49 100644
--- a/src/cmd/5c/reg.c
+++ b/src/cmd/5c/reg.c
@@ -406,7 +406,7 @@ loop2:
rgp->cost = change;
nregion++;
if(nregion >= NRGN) {
- warn(Z, "too many regions");
+ fatal(Z, "too many regions");
goto brk;
}
rgp++;
@@ -642,11 +642,8 @@ mkvar(Addr *a, int docon)
if(s)
if(s->name[0] == '.')
goto none;
- if(nvar >= NVAR) {
- if(debug['w'] > 1 && s)
- warn(Z, "variable not optimized: %s", s->name);
- goto none;
- }
+ if(nvar >= NVAR)
+ fatal(Z, "variable not optimized: %s", s->name);
i = nvar;
nvar++;
v = &var[i];
diff --git a/src/cmd/6a/lex.c b/src/cmd/6a/lex.c
index b50e1622e..8973d6974 100644
--- a/src/cmd/6a/lex.c
+++ b/src/cmd/6a/lex.c
@@ -101,6 +101,7 @@ main(int argc, char *argv[])
ctxt = linknew(thelinkarch);
ctxt->diag = yyerror;
ctxt->bso = &bstdout;
+ ctxt->enforce_data_order = 1;
Binit(&bstdout, 1, OWRITE);
listinit6();
fmtinstall('L', Lconv);
diff --git a/src/cmd/6c/reg.c b/src/cmd/6c/reg.c
index 348d747b7..6f8d3ce14 100644
--- a/src/cmd/6c/reg.c
+++ b/src/cmd/6c/reg.c
@@ -585,14 +585,11 @@ loop2:
}
rgp->cost = change;
nregion++;
- if(nregion >= NRGN) {
- warn(Z, "too many regions");
- goto brk;
- }
+ if(nregion >= NRGN)
+ fatal(Z, "too many regions");
rgp++;
}
}
-brk:
qsort(region, nregion, sizeof(region[0]), rcmp);
/*
@@ -808,11 +805,8 @@ mkvar(Reg *r, Addr *a)
goto out;
v++;
}
- if(nvar >= NVAR) {
- if(debug['w'] > 1 && s)
- warn(Z, "variable not optimized: %s", s->name);
- goto none;
- }
+ if(nvar >= NVAR)
+ fatal(Z, "variable not optimized: %s", s->name);
i = nvar;
nvar++;
v = &var[i];
diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c
index 807e48cb5..6ce6a18ab 100644
--- a/src/cmd/8a/lex.c
+++ b/src/cmd/8a/lex.c
@@ -90,6 +90,7 @@ main(int argc, char *argv[])
ctxt = linknew(&link386);
ctxt->diag = yyerror;
ctxt->bso = &bstdout;
+ ctxt->enforce_data_order = 1;
Binit(&bstdout, 1, OWRITE);
listinit8();
fmtinstall('L', Lconv);
diff --git a/src/cmd/8c/reg.c b/src/cmd/8c/reg.c
index e6ba8bcb3..ea862f388 100644
--- a/src/cmd/8c/reg.c
+++ b/src/cmd/8c/reg.c
@@ -518,7 +518,7 @@ loop2:
rgp->cost = change;
nregion++;
if(nregion >= NRGN) {
- warn(Z, "too many regions");
+ fatal(Z, "too many regions");
goto brk;
}
rgp++;
@@ -746,11 +746,8 @@ mkvar(Reg *r, Addr *a)
goto out;
v++;
}
- if(nvar >= NVAR) {
- if(debug['w'] > 1 && s)
- warn(Z, "variable not optimized: %s", s->name);
- goto none;
- }
+ if(nvar >= NVAR)
+ fatal(Z, "variable not optimized: %s", s->name);
i = nvar;
nvar++;
v = &var[i];
diff --git a/src/cmd/cc/godefs.c b/src/cmd/cc/godefs.c
index d3ab52fde..d9f67f0ae 100644
--- a/src/cmd/cc/godefs.c
+++ b/src/cmd/cc/godefs.c
@@ -353,8 +353,10 @@ godefvar(Sym *s)
case CSTATIC:
case CEXTERN:
case CGLOBL:
- if(strchr(s->name, '$') != nil) // TODO(lvd)
- break;
+ if(strchr(s->name, '$') != nil)
+ break;
+ if(strncmp(s->name, "go.weak.", 8) == 0)
+ break;
Bprint(&outbuf, "var %U\t", s->name);
printtypename(t);
Bprint(&outbuf, "\n");
diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c
index ee1ac1da4..5fbb4f0cf 100644
--- a/src/cmd/gc/builtin.c
+++ b/src/cmd/gc/builtin.c
@@ -84,9 +84,12 @@ char *runtimeimport =
"func @\"\".chansend1 (@\"\".chanType·1 *byte, @\"\".hchan·2 chan<- any, @\"\".elem·3 *any)\n"
"func @\"\".closechan (@\"\".hchan·1 any)\n"
"func @\"\".writebarrierptr (@\"\".dst·1 *any, @\"\".src·2 any)\n"
- "func @\"\".writebarrieriface (@\"\".dst·1 *any, @\"\".src·2 any)\n"
"func @\"\".writebarrierstring (@\"\".dst·1 *any, @\"\".src·2 any)\n"
"func @\"\".writebarrierslice (@\"\".dst·1 *any, @\"\".src·2 any)\n"
+ "func @\"\".writebarrieriface (@\"\".dst·1 *any, @\"\".src·2 any)\n"
+ "func @\"\".writebarrierfat2 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n"
+ "func @\"\".writebarrierfat3 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n"
+ "func @\"\".writebarrierfat4 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n"
"func @\"\".writebarrierfat (@\"\".typ·1 *byte, @\"\".dst·2 *any, @\"\".src·3 *any)\n"
"func @\"\".selectnbsend (@\"\".chanType·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (? bool)\n"
"func @\"\".selectnbrecv (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".hchan·4 <-chan any) (? bool)\n"
diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c
index 73c2581be..dfcf47520 100644
--- a/src/cmd/gc/dcl.c
+++ b/src/cmd/gc/dcl.c
@@ -488,6 +488,10 @@ colasdefn(NodeList *left, Node *defn)
NodeList *l;
Node *n;
+ for(l=left; l; l=l->next)
+ if(l->n->sym != S)
+ l->n->sym->flags |= SymUniq;
+
nnew = 0;
nerr = 0;
for(l=left; l; l=l->next) {
@@ -499,6 +503,13 @@ colasdefn(NodeList *left, Node *defn)
nerr++;
continue;
}
+ if((n->sym->flags & SymUniq) == 0) {
+ yyerrorl(defn->lineno, "%S repeated on left side of :=", n->sym);
+ n->diag++;
+ nerr++;
+ continue;
+ }
+ n->sym->flags &= ~SymUniq;
if(n->sym->block == block)
continue;
@@ -547,6 +558,9 @@ ifacedcl(Node *n)
if(n->op != ODCLFIELD || n->right == N)
fatal("ifacedcl");
+ if(isblank(n->left))
+ yyerror("methods must have a unique non-blank name");
+
dclcontext = PPARAM;
markdcl();
funcdepth++;
diff --git a/src/cmd/gc/order.c b/src/cmd/gc/order.c
index 3027ed27d..76820fde7 100644
--- a/src/cmd/gc/order.c
+++ b/src/cmd/gc/order.c
@@ -438,6 +438,9 @@ ordercall(Node *n, Order *order)
// cases they are also typically registerizable, so not much harm done.
// And this only applies to the multiple-assignment form.
// We could do a more precise analysis if needed, like in walk.c.
+//
+// Ordermapassign also inserts these temporaries if needed for
+// calling writebarrierfat with a pointer to n->right.
static void
ordermapassign(Node *n, Order *order)
{
@@ -451,7 +454,8 @@ ordermapassign(Node *n, Order *order)
case OAS:
order->out = list(order->out, n);
- if((n->left->op == OINDEXMAP || (needwritebarrier(n->left, n->right) && n->left->type->width > widthptr)) && !isaddrokay(n->right)) {
+ // We call writebarrierfat only for values > 4 pointers long. See walk.c.
+ if((n->left->op == OINDEXMAP || (needwritebarrier(n->left, n->right) && n->left->type->width > 4*widthptr)) && !isaddrokay(n->right)) {
m = n->left;
n->left = ordertemp(m->type, order, 0);
a = nod(OAS, m, n->left);
diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c
index 50c03788e..39028e3f8 100644
--- a/src/cmd/gc/pgen.c
+++ b/src/cmd/gc/pgen.c
@@ -182,6 +182,8 @@ compile(Node *fn)
yyerror("missing function body", fn);
goto ret;
}
+ if(debug['A'])
+ goto ret;
emitptrargsmap();
goto ret;
}
diff --git a/src/cmd/gc/racewalk.c b/src/cmd/gc/racewalk.c
index cb98ca247..c9e27fe56 100644
--- a/src/cmd/gc/racewalk.c
+++ b/src/cmd/gc/racewalk.c
@@ -210,12 +210,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
case OCALLFUNC:
// Instrument dst argument of runtime.writebarrier* calls
// as we do not instrument runtime code.
- if(n->left->sym != S && n->left->sym->pkg == runtimepkg &&
- (strcmp(n->left->sym->name, "writebarrierptr") == 0 ||
- strcmp(n->left->sym->name, "writebarrierstring") == 0 ||
- strcmp(n->left->sym->name, "writebarrierslice") == 0 ||
- strcmp(n->left->sym->name, "writebarrieriface") == 0 ||
- strcmp(n->left->sym->name, "writebarrierfat") == 0)) {
+ if(n->left->sym != S && n->left->sym->pkg == runtimepkg && strncmp(n->left->sym->name, "writebarrier", 12) == 0) {
// Find the dst argument.
// The list can be reordered, so it's not necessary just the first or the second element.
for(l = n->list; l; l = l->next) {
diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c
index e229b3075..0f8802abc 100644
--- a/src/cmd/gc/reflect.c
+++ b/src/cmd/gc/reflect.c
@@ -716,9 +716,10 @@ static int
dcommontype(Sym *s, int ot, Type *t)
{
int i, alg, sizeofAlg, gcprog;
- Sym *sptr, *algsym, *zero, *gcprog0, *gcprog1;
+ Sym *sptr, *algsym, *zero, *gcprog0, *gcprog1, *sbits;
uint8 gcmask[16];
static Sym *algarray;
+ uint64 x1, x2;
char *p;
if(ot != 0)
@@ -804,8 +805,26 @@ dcommontype(Sym *s, int ot, Type *t)
ot = dsymptr(s, ot, gcprog1, 0);
} else {
gengcmask(t, gcmask);
- for(i = 0; i < 2*widthptr; i++)
- ot = duint8(s, ot, gcmask[i]);
+ x1 = 0;
+ for(i=0; i<8; i++)
+ x1 = x1<<8 | gcmask[i];
+ if(widthptr == 4) {
+ p = smprint("gcbits.%#016llux", x1);
+ } else {
+ x2 = 0;
+ for(i=0; i<8; i++)
+ x2 = x2<<8 | gcmask[i+8];
+ p = smprint("gcbits.%#016llux%016llux", x1, x2);
+ }
+ sbits = pkglookup(p, runtimepkg);
+ if((sbits->flags & SymUniq) == 0) {
+ sbits->flags |= SymUniq;
+ for(i = 0; i < 2*widthptr; i++)
+ duint8(sbits, i, gcmask[i]);
+ ggloblsym(sbits, 2*widthptr, DUPOK|RODATA);
+ }
+ ot = dsymptr(s, ot, sbits, 0);
+ ot = duintptr(s, ot, 0);
}
p = smprint("%-uT", t);
//print("dcommontype: %s\n", p);
diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go
index fa927a58a..86afe67f1 100644
--- a/src/cmd/gc/runtime.go
+++ b/src/cmd/gc/runtime.go
@@ -112,6 +112,13 @@ func writebarrierptr(dst *any, src any)
func writebarrierstring(dst *any, src any)
func writebarrierslice(dst *any, src any)
func writebarrieriface(dst *any, src any)
+
+// The unused *byte argument makes sure that src is 2-pointer-aligned,
+// which is the maximum alignment on NaCl amd64p32
+// (and possibly on 32-bit systems if we start 64-bit aligning uint64s).
+func writebarrierfat2(dst *any, _ *byte, src any)
+func writebarrierfat3(dst *any, _ *byte, src any)
+func writebarrierfat4(dst *any, _ *byte, src any)
func writebarrierfat(typ *byte, dst *any, src *any)
func selectnbsend(chanType *byte, hchan chan<- any, elem *any) bool
diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c
index 508747e5a..f050026d9 100644
--- a/src/cmd/gc/sinit.c
+++ b/src/cmd/gc/sinit.c
@@ -207,7 +207,7 @@ init2(Node *n, NodeList **out)
if(n->op == OCLOSURE)
init2list(n->closure->nbody, out);
- if(n->op == ODOTMETH)
+ if(n->op == ODOTMETH || n->op == OCALLPART)
init2(n->type->nname, out);
}
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
index 713348c0c..241d7d74a 100644
--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -2040,30 +2040,42 @@ static Node*
applywritebarrier(Node *n, NodeList **init)
{
Node *l, *r;
+ Type *t;
if(n->left && n->right && needwritebarrier(n->left, n->right)) {
+ t = n->left->type;
l = nod(OADDR, n->left, N);
l->etype = 1; // addr does not escape
- if(n->left->type->width == widthptr) {
- n = mkcall1(writebarrierfn("writebarrierptr", n->left->type, n->right->type), T, init,
+ if(t->width == widthptr) {
+ n = mkcall1(writebarrierfn("writebarrierptr", t, n->right->type), T, init,
l, n->right);
- } else if(n->left->type->etype == TSTRING) {
- n = mkcall1(writebarrierfn("writebarrierstring", n->left->type, n->right->type), T, init,
+ } else if(t->etype == TSTRING) {
+ n = mkcall1(writebarrierfn("writebarrierstring", t, n->right->type), T, init,
l, n->right);
- } else if(isslice(n->left->type)) {
- n = mkcall1(writebarrierfn("writebarrierslice", n->left->type, n->right->type), T, init,
+ } else if(isslice(t)) {
+ n = mkcall1(writebarrierfn("writebarrierslice", t, n->right->type), T, init,
l, n->right);
- } else if(isinter(n->left->type)) {
- n = mkcall1(writebarrierfn("writebarrieriface", n->left->type, n->right->type), T, init,
+ } else if(isinter(t)) {
+ n = mkcall1(writebarrierfn("writebarrieriface", t, n->right->type), T, init,
l, n->right);
+ } else if(t->width == 2*widthptr) {
+ n = mkcall1(writebarrierfn("writebarrierfat2", t, n->right->type), T, init,
+ l, nodnil(), n->right);
+ } else if(t->width == 3*widthptr) {
+ n = mkcall1(writebarrierfn("writebarrierfat3", t, n->right->type), T, init,
+ l, nodnil(), n->right);
+ } else if(t->width == 4*widthptr) {
+ n = mkcall1(writebarrierfn("writebarrierfat4", t, n->right->type), T, init,
+ l, nodnil(), n->right);
} else {
r = n->right;
while(r->op == OCONVNOP)
r = r->left;
r = nod(OADDR, r, N);
r->etype = 1; // addr does not escape
- n = mkcall1(writebarrierfn("writebarrierfat", n->left->type, r->left->type), T, init,
- typename(n->left->type), l, r);
+ //warnl(n->lineno, "writebarrierfat %T %N", t, r);
+ n = mkcall1(writebarrierfn("writebarrierfat", t, r->left->type), T, init,
+ typename(t), l, r);
}
}
return n;
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index 9c7b42650..49b84709e 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -1852,7 +1852,7 @@ func (gccgoToolchain) linker() string {
}
func (gccgoToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) {
- out := p.Name + ".o"
+ out := "_go_.o"
ofile = obj + out
gcargs := []string{"-g"}
gcargs = append(gcargs, b.gccArchArgs()...)
diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go
index 8e2facd04..314c69bd8 100644
--- a/src/cmd/go/doc.go
+++ b/src/cmd/go/doc.go
@@ -385,28 +385,29 @@ syntax of package template. The default output is equivalent to -f
'{{.ImportPath}}'. The struct being passed to the template is:
type Package struct {
- Dir string // directory containing package sources
- ImportPath string // import path of package in dir
- Name string // package name
- Doc string // package documentation string
- Target string // install path
- Goroot bool // is this package in the Go root?
- Standard bool // is this package part of the standard Go library?
- Stale bool // would 'go install' do anything for this package?
- Root string // Go root or Go path dir containing this package
+ Dir string // directory containing package sources
+ ImportPath string // import path of package in dir
+ ImportComment string // path in import comment on package statement
+ Name string // package name
+ Doc string // package documentation string
+ Target string // install path
+ Goroot bool // is this package in the Go root?
+ Standard bool // is this package part of the standard Go library?
+ Stale bool // would 'go install' do anything for this package?
+ Root string // Go root or Go path dir containing this package
// Source files
- GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
- CgoFiles []string // .go sources files that import "C"
+ GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string // .go sources files that import "C"
IgnoredGoFiles []string // .go sources ignored due to build constraints
- CFiles []string // .c source files
- CXXFiles []string // .cc, .cxx and .cpp source files
- MFiles []string // .m source files
- HFiles []string // .h, .hh, .hpp and .hxx source files
- SFiles []string // .s source files
- SwigFiles []string // .swig files
- SwigCXXFiles []string // .swigcxx files
- SysoFiles []string // .syso object files to add to archive
+ CFiles []string // .c source files
+ CXXFiles []string // .cc, .cxx and .cpp source files
+ MFiles []string // .m source files
+ HFiles []string // .h, .hh, .hpp and .hxx source files
+ SFiles []string // .s source files
+ SwigFiles []string // .swig files
+ SwigCXXFiles []string // .swigcxx files
+ SysoFiles []string // .syso object files to add to archive
// Cgo directives
CgoCFLAGS []string // cgo: flags for C compiler
diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go
index 0ead43502..fbf96167f 100644
--- a/src/cmd/go/list.go
+++ b/src/cmd/go/list.go
@@ -30,28 +30,29 @@ syntax of package template. The default output is equivalent to -f
'{{.ImportPath}}'. The struct being passed to the template is:
type Package struct {
- Dir string // directory containing package sources
- ImportPath string // import path of package in dir
- Name string // package name
- Doc string // package documentation string
- Target string // install path
- Goroot bool // is this package in the Go root?
- Standard bool // is this package part of the standard Go library?
- Stale bool // would 'go install' do anything for this package?
- Root string // Go root or Go path dir containing this package
+ Dir string // directory containing package sources
+ ImportPath string // import path of package in dir
+ ImportComment string // path in import comment on package statement
+ Name string // package name
+ Doc string // package documentation string
+ Target string // install path
+ Goroot bool // is this package in the Go root?
+ Standard bool // is this package part of the standard Go library?
+ Stale bool // would 'go install' do anything for this package?
+ Root string // Go root or Go path dir containing this package
// Source files
- GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
- CgoFiles []string // .go sources files that import "C"
+ GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string // .go sources files that import "C"
IgnoredGoFiles []string // .go sources ignored due to build constraints
- CFiles []string // .c source files
- CXXFiles []string // .cc, .cxx and .cpp source files
- MFiles []string // .m source files
- HFiles []string // .h, .hh, .hpp and .hxx source files
- SFiles []string // .s source files
- SwigFiles []string // .swig files
- SwigCXXFiles []string // .swigcxx files
- SysoFiles []string // .syso object files to add to archive
+ CFiles []string // .c source files
+ CXXFiles []string // .cc, .cxx and .cpp source files
+ MFiles []string // .m source files
+ HFiles []string // .h, .hh, .hpp and .hxx source files
+ SFiles []string // .s source files
+ SwigFiles []string // .swig files
+ SwigCXXFiles []string // .swigcxx files
+ SysoFiles []string // .syso object files to add to archive
// Cgo directives
CgoCFLAGS []string // cgo: flags for C compiler
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index 7f7a3b04f..e17326442 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -26,16 +26,17 @@ type Package struct {
// Note: These fields are part of the go command's public API.
// See list.go. It is okay to add fields, but not to change or
// remove existing ones. Keep in sync with list.go
- Dir string `json:",omitempty"` // directory containing package sources
- ImportPath string `json:",omitempty"` // import path of package in dir
- Name string `json:",omitempty"` // package name
- Doc string `json:",omitempty"` // package documentation string
- Target string `json:",omitempty"` // install path
- Goroot bool `json:",omitempty"` // is this package found in the Go root?
- Standard bool `json:",omitempty"` // is this package part of the standard Go library?
- Stale bool `json:",omitempty"` // would 'go install' do anything for this package?
- Root string `json:",omitempty"` // Go root or Go path dir containing this package
- ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory
+ Dir string `json:",omitempty"` // directory containing package sources
+ ImportPath string `json:",omitempty"` // import path of package in dir
+ ImportComment string `json:",omitempty"` // path in import comment on package statement
+ Name string `json:",omitempty"` // package name
+ Doc string `json:",omitempty"` // package documentation string
+ Target string `json:",omitempty"` // install path
+ Goroot bool `json:",omitempty"` // is this package found in the Go root?
+ Standard bool `json:",omitempty"` // is this package part of the standard Go library?
+ Stale bool `json:",omitempty"` // would 'go install' do anything for this package?
+ Root string `json:",omitempty"` // Go root or Go path dir containing this package
+ ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory
// Source files
GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
@@ -104,6 +105,7 @@ func (p *Package) copyBuild(pp *build.Package) {
p.Dir = pp.Dir
p.ImportPath = pp.ImportPath
+ p.ImportComment = pp.ImportComment
p.Name = pp.Name
p.Doc = pp.Doc
p.Root = pp.Root
diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash
index 6a72bcde0..652ef3b5b 100755
--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -1085,6 +1085,20 @@ fi
unset GOPATH
rm -rf $d
+TEST go vet with external tests
+d=$(mktemp -d -t testgoXXX)
+export GOPATH=$(pwd)/testdata
+if ./testgo vet vetpkg >$d/err 2>&1; then
+ echo "go vet vetpkg passes incorrectly"
+ ok=false
+elif ! grep -q 'missing argument for Printf' $d/err; then
+ echo "go vet vetpkg did not find missing argument for Printf"
+ cat $d/err
+ ok=false
+fi
+unset GOPATH
+rm -rf $d
+
# clean up
if $started; then stop; fi
rm -rf testdata/bin testdata/bin1
diff --git a/src/cmd/go/testdata/src/vetpkg/a_test.go b/src/cmd/go/testdata/src/vetpkg/a_test.go
new file mode 100644
index 000000000..9b64e8e1a
--- /dev/null
+++ b/src/cmd/go/testdata/src/vetpkg/a_test.go
@@ -0,0 +1 @@
+package p_test
diff --git a/src/cmd/go/testdata/src/vetpkg/b.go b/src/cmd/go/testdata/src/vetpkg/b.go
new file mode 100644
index 000000000..99e18f63d
--- /dev/null
+++ b/src/cmd/go/testdata/src/vetpkg/b.go
@@ -0,0 +1,7 @@
+package p
+
+import "fmt"
+
+func f() {
+ fmt.Printf("%d")
+}
diff --git a/src/cmd/go/vet.go b/src/cmd/go/vet.go
index ffb431837..de7befc61 100644
--- a/src/cmd/go/vet.go
+++ b/src/cmd/go/vet.go
@@ -4,6 +4,8 @@
package main
+import "path/filepath"
+
func init() {
addBuildFlagsNX(cmdVet)
}
@@ -28,10 +30,21 @@ See also: go fmt, go fix.
}
func runVet(cmd *Command, args []string) {
- for _, pkg := range packages(args) {
- // Use pkg.gofiles instead of pkg.Dir so that
- // the command only applies to this package,
- // not to packages in subdirectories.
- run(tool("vet"), relPaths(stringList(pkg.gofiles, pkg.sfiles)))
+ for _, p := range packages(args) {
+ // Vet expects to be given a set of files all from the same package.
+ // Run once for package p and once for package p_test.
+ if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles) > 0 {
+ runVetFiles(p, stringList(p.GoFiles, p.CgoFiles, p.TestGoFiles, p.SFiles))
+ }
+ if len(p.XTestGoFiles) > 0 {
+ runVetFiles(p, stringList(p.XTestGoFiles))
+ }
+ }
+}
+
+func runVetFiles(p *Package, files []string) {
+ for i := range files {
+ files[i] = filepath.Join(p.Dir, files[i])
}
+ run(tool("vet"), relPaths(files))
}
diff --git a/src/cmd/ld/decodesym.c b/src/cmd/ld/decodesym.c
index c53066942..037263dce 100644
--- a/src/cmd/ld/decodesym.c
+++ b/src/cmd/ld/decodesym.c
@@ -111,7 +111,10 @@ decodetype_gcprog(LSym *s)
uint8*
decodetype_gcmask(LSym *s)
{
- return (uint8*)(s->p + 1*PtrSize + 8 + 1*PtrSize);
+ LSym *mask;
+
+ mask = decode_reloc_sym(s, 1*PtrSize + 8 + 1*PtrSize);
+ return mask->p;
}
// Type.ArrayType.elem and Type.SliceType.Elem
diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c
index 4efb0ed53..a3ba52325 100644
--- a/src/cmd/ld/dwarf.c
+++ b/src/cmd/ld/dwarf.c
@@ -1733,6 +1733,7 @@ writeframes(void)
LSym *s;
vlong fdeo, fdesize, pad;
Pciter pcsp;
+ uint32 nextpc;
if(framesec == S)
framesec = linklookup(ctxt, ".dwarfframe", 0);
@@ -1775,8 +1776,17 @@ writeframes(void)
addrput(0); // initial location
addrput(0); // address range
- for(pciterinit(ctxt, &pcsp, &s->pcln->pcsp); !pcsp.done; pciternext(&pcsp))
- putpccfadelta(pcsp.nextpc - pcsp.pc, PtrSize + pcsp.value);
+ for(pciterinit(ctxt, &pcsp, &s->pcln->pcsp); !pcsp.done; pciternext(&pcsp)) {
+ nextpc = pcsp.nextpc;
+ // pciterinit goes up to the end of the function,
+ // but DWARF expects us to stop just before the end.
+ if(nextpc == s->size) {
+ nextpc--;
+ if(nextpc < pcsp.pc)
+ continue;
+ }
+ putpccfadelta(nextpc - pcsp.pc, PtrSize + pcsp.value);
+ }
fdesize = cpos() - fdeo - 4; // exclude the length field.
pad = rnd(fdesize, PtrSize) - fdesize;
diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c
index 38e414755..35f8b4985 100644
--- a/src/cmd/ld/ldelf.c
+++ b/src/cmd/ld/ldelf.c
@@ -582,6 +582,8 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn)
continue;
sect = obj->sect+sym.shndx;
if(sect->sym == nil) {
+ if(strncmp(sym.name, ".Linfo_string", 13) == 0) // clang does this
+ continue;
diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type);
continue;
}
diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c
index 1b0591614..4f5e51f2f 100644
--- a/src/cmd/ld/ldpe.c
+++ b/src/cmd/ld/ldpe.c
@@ -128,6 +128,7 @@ struct PeObj {
};
static int map(PeObj *obj, PeSect *sect);
+static int issect(PeSym *s);
static int readsym(PeObj *obj, int i, PeSym **sym);
void
@@ -179,6 +180,15 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
Bseek(f, base+obj->fh.PointerToSymbolTable+sizeof(symbuf)*obj->fh.NumberOfSymbols, 0);
if(Bread(f, obj->snames, l) != l)
goto bad;
+ // rewrite section names if they start with /
+ for(i=0; i < obj->fh.NumberOfSections; i++) {
+ if(obj->sect[i].name == nil)
+ continue;
+ if(obj->sect[i].name[0] != '/')
+ continue;
+ l = atoi(obj->sect[i].name + 1);
+ obj->sect[i].name = (char*)&obj->snames[l];
+ }
// read symbols
obj->pesym = mal(obj->fh.NumberOfSymbols*sizeof obj->pesym[0]);
obj->npesym = obj->fh.NumberOfSymbols;
@@ -309,8 +319,8 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
// ld -r could generate multiple section symbols for the
// same section but with different values, we have to take
// that into account
- if (obj->pesym[symindex].name[0] == '.')
- rp->add += obj->pesym[symindex].value;
+ if(issect(&obj->pesym[symindex]))
+ rp->add += obj->pesym[symindex].value;
}
qsort(r, rsect->sh.NumberOfRelocations, sizeof r[0], rbyoff);
@@ -318,12 +328,12 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
s->r = r;
s->nr = rsect->sh.NumberOfRelocations;
}
-
+
// enter sub-symbols into symbol table.
for(i=0; i<obj->npesym; i++) {
if(obj->pesym[i].name == 0)
continue;
- if(obj->pesym[i].name[0] == '.') //skip section
+ if(issect(&obj->pesym[i]))
continue;
if(obj->pesym[i].sectnum > 0) {
sect = &obj->sect[obj->pesym[i].sectnum-1];
@@ -422,6 +432,12 @@ map(PeObj *obj, PeSect *sect)
}
static int
+issect(PeSym *s)
+{
+ return s->sclass == IMAGE_SYM_CLASS_STATIC && s->type == 0 && s->name[0] == '.';
+}
+
+static int
readsym(PeObj *obj, int i, PeSym **y)
{
LSym *s;
@@ -436,7 +452,7 @@ readsym(PeObj *obj, int i, PeSym **y)
sym = &obj->pesym[i];
*y = sym;
- if(sym->name[0] == '.') // .section
+ if(issect(sym))
name = obj->sect[sym->sectnum-1].sym->name;
else {
name = sym->name;
diff --git a/src/cmd/ld/macho.c b/src/cmd/ld/macho.c
index 61306bb7c..fe7e10e46 100644
--- a/src/cmd/ld/macho.c
+++ b/src/cmd/ld/macho.c
@@ -590,8 +590,7 @@ machosymtab(void)
if(strstr(s->extname, "·") == nil) {
addstring(symstr, s->extname);
} else {
- p = s->extname;
- while (*p++ != '\0') {
+ for(p = s->extname; *p; p++) {
if((uchar)*p == 0xc2 && (uchar)*(p+1) == 0xb7) {
adduint8(ctxt, symstr, '.');
p++;
diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go
index 6e57e913a..7a37b98e3 100644
--- a/src/crypto/x509/x509.go
+++ b/src/crypto/x509/x509.go
@@ -494,6 +494,11 @@ type Certificate struct {
BasicConstraintsValid bool // if true then the next two fields are valid.
IsCA bool
MaxPathLen int
+ // MaxPathLenZero indicates that BasicConstraintsValid==true and
+ // MaxPathLen==0 should be interpreted as an actual maximum path length
+ // of zero. Otherwise, that combination is interpreted as MaxPathLen
+ // not being set.
+ MaxPathLenZero bool
SubjectKeyId []byte
AuthorityKeyId []byte
@@ -913,6 +918,7 @@ func parseCertificate(in *certificate) (*Certificate, error) {
out.BasicConstraintsValid = true
out.IsCA = constraints.IsCA
out.MaxPathLen = constraints.MaxPathLen
+ out.MaxPathLenZero = out.MaxPathLen == 0
continue
}
case 17:
@@ -1227,8 +1233,15 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
}
if template.BasicConstraintsValid && !oidInExtensions(oidExtensionBasicConstraints, template.ExtraExtensions) {
+ // Leaving MaxPathLen as zero indicates that no maximum path
+ // length is desired, unless MaxPathLenZero is set. A value of
+ // -1 causes encoding/asn1 to omit the value as desired.
+ maxPathLen := template.MaxPathLen
+ if maxPathLen == 0 && !template.MaxPathLenZero {
+ maxPathLen = -1
+ }
ret[n].Id = oidExtensionBasicConstraints
- ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, template.MaxPathLen})
+ ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, maxPathLen})
ret[n].Critical = true
if err != nil {
return
@@ -1657,7 +1670,7 @@ var oidExtensionRequest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 14}
// CreateCertificateRequest creates a new certificate based on a template. The
// following members of template are used: Subject, Attributes,
-// SignatureAlgorithm, Extension, DNSNames, EmailAddresses, and IPAddresses.
+// SignatureAlgorithm, Extensions, DNSNames, EmailAddresses, and IPAddresses.
// The private key is the private key of the signer.
//
// The returned slice is the certificate request in DER encoding.
diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go
index abe86216f..4f5173fb5 100644
--- a/src/crypto/x509/x509_test.go
+++ b/src/crypto/x509/x509_test.go
@@ -953,6 +953,69 @@ func TestParseCertificateRequest(t *testing.T) {
}
}
+func TestMaxPathLen(t *testing.T) {
+ block, _ := pem.Decode([]byte(pemPrivateKey))
+ rsaPriv, err := ParsePKCS1PrivateKey(block.Bytes)
+ if err != nil {
+ t.Fatalf("Failed to parse private key: %s", err)
+ }
+
+ template := &Certificate{
+ SerialNumber: big.NewInt(1),
+ Subject: pkix.Name{
+ CommonName: "Σ Acme Co",
+ },
+ NotBefore: time.Unix(1000, 0),
+ NotAfter: time.Unix(100000, 0),
+
+ BasicConstraintsValid: true,
+ IsCA: true,
+ }
+
+ serialiseAndParse := func(template *Certificate) *Certificate {
+ derBytes, err := CreateCertificate(rand.Reader, template, template, &rsaPriv.PublicKey, rsaPriv)
+ if err != nil {
+ t.Fatalf("failed to create certificate: %s", err)
+ return nil
+ }
+
+ cert, err := ParseCertificate(derBytes)
+ if err != nil {
+ t.Fatalf("failed to parse certificate: %s", err)
+ return nil
+ }
+
+ return cert
+ }
+
+ cert1 := serialiseAndParse(template)
+ if m := cert1.MaxPathLen; m != -1 {
+ t.Errorf("Omitting MaxPathLen didn't turn into -1, got %d", m)
+ }
+ if cert1.MaxPathLenZero {
+ t.Errorf("Omitting MaxPathLen resulted in MaxPathLenZero")
+ }
+
+ template.MaxPathLen = 1
+ cert2 := serialiseAndParse(template)
+ if m := cert2.MaxPathLen; m != 1 {
+ t.Errorf("Setting MaxPathLen didn't work. Got %d but set 1", m)
+ }
+ if cert2.MaxPathLenZero {
+ t.Errorf("Setting MaxPathLen resulted in MaxPathLenZero")
+ }
+
+ template.MaxPathLen = 0
+ template.MaxPathLenZero = true
+ cert3 := serialiseAndParse(template)
+ if m := cert3.MaxPathLen; m != 0 {
+ t.Errorf("Setting MaxPathLenZero didn't work, got %d", m)
+ }
+ if !cert3.MaxPathLenZero {
+ t.Errorf("Setting MaxPathLen to zero didn't result in MaxPathLenZero")
+ }
+}
+
// This CSR was generated with OpenSSL:
// openssl req -out CSR.csr -new -newkey rsa:2048 -nodes -keyout privateKey.key -config openssl.cnf
//
diff --git a/src/database/sql/fakedb_test.go b/src/database/sql/fakedb_test.go
index c7db0dd77..171c322d4 100644
--- a/src/database/sql/fakedb_test.go
+++ b/src/database/sql/fakedb_test.go
@@ -10,6 +10,7 @@ import (
"fmt"
"io"
"log"
+ "sort"
"strconv"
"strings"
"sync"
@@ -126,6 +127,27 @@ func init() {
Register("test", fdriver)
}
+func contains(list []string, y string) bool {
+ for _, x := range list {
+ if x == y {
+ return true
+ }
+ }
+ return false
+}
+
+type Dummy struct {
+ driver.Driver
+}
+
+func TestDrivers(t *testing.T) {
+ Register("invalid", Dummy{})
+ all := Drivers()
+ if len(all) < 2 || !sort.StringsAreSorted(all) || !contains(all, "test") || !contains(all, "invalid") {
+ t.Fatalf("Drivers = %v, want sorted list with at least [invalid, test]", all)
+ }
+}
+
// Supports dsn forms:
// <dbname>
// <dbname>;<opts> (only currently supported option is `badConn`,
diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go
index 731b7a7f7..ad9179cf7 100644
--- a/src/database/sql/sql.go
+++ b/src/database/sql/sql.go
@@ -18,6 +18,7 @@ import (
"fmt"
"io"
"runtime"
+ "sort"
"sync"
)
@@ -36,6 +37,16 @@ func Register(name string, driver driver.Driver) {
drivers[name] = driver
}
+// Drivers returns a sorted list of the names of the registered drivers.
+func Drivers() []string {
+ var list []string
+ for name := range drivers {
+ list = append(list, name)
+ }
+ sort.Strings(list)
+ return list
+}
+
// RawBytes is a byte slice that holds a reference to memory owned by
// the database itself. After a Scan into a RawBytes, the slice is only
// valid until the next call to Next, Scan, or Close.
diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go
index c908e7a88..de8a3a24f 100644
--- a/src/debug/elf/file.go
+++ b/src/debug/elf/file.go
@@ -564,6 +564,10 @@ func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
continue
}
+ // There are relocations, so this must be a normal
+ // object file, and we only look at section symbols,
+ // so we assume that the symbol value is 0.
+
switch t {
case R_X86_64_64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
@@ -646,6 +650,10 @@ func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error {
continue
}
+ // There are relocations, so this must be a normal
+ // object file, and we only look at section symbols,
+ // so we assume that the symbol value is 0.
+
switch t {
case R_AARCH64_ABS64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go
index 3aeb3dcc4..8b3d1b341 100644
--- a/src/encoding/asn1/asn1.go
+++ b/src/encoding/asn1/asn1.go
@@ -640,7 +640,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
// when it sees a string, so if we see a different string type on the
// wire, we change the universal type to match.
if universalTag == tagPrintableString {
- if params.tag == nil {
+ if t.class == classUniversal {
switch t.tag {
case tagIA5String, tagGeneralString, tagT61String, tagUTF8String:
universalTag = t.tag
@@ -652,7 +652,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
// Special case for time: UTCTime and GeneralizedTime both map to the
// Go type time.Time.
- if universalTag == tagUTCTime && params.tag == nil && t.tag == tagGeneralizedTime {
+ if universalTag == tagUTCTime && t.tag == tagGeneralizedTime && t.class == classUniversal {
universalTag = tagGeneralizedTime
}
diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go
index b94d59d36..4e864d08a 100644
--- a/src/encoding/asn1/asn1_test.go
+++ b/src/encoding/asn1/asn1_test.go
@@ -817,3 +817,51 @@ func TestStringSlice(t *testing.T) {
}
}
}
+
+type explicitTaggedTimeTest struct {
+ Time time.Time `asn1:"explicit,tag:0"`
+}
+
+var explicitTaggedTimeTestData = []struct {
+ in []byte
+ out explicitTaggedTimeTest
+}{
+ {[]byte{0x30, 0x11, 0xa0, 0xf, 0x17, 0xd, '9', '1', '0', '5', '0', '6', '1', '6', '4', '5', '4', '0', 'Z'},
+ explicitTaggedTimeTest{time.Date(1991, 05, 06, 16, 45, 40, 0, time.UTC)}},
+ {[]byte{0x30, 0x17, 0xa0, 0xf, 0x18, 0x13, '2', '0', '1', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '+', '0', '6', '0', '7'},
+ explicitTaggedTimeTest{time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", 6*60*60+7*60))}},
+}
+
+func TestExplicitTaggedTime(t *testing.T) {
+ // Test that a time.Time will match either tagUTCTime or
+ // tagGeneralizedTime.
+ for i, test := range explicitTaggedTimeTestData {
+ var got explicitTaggedTimeTest
+ _, err := Unmarshal(test.in, &got)
+ if err != nil {
+ t.Errorf("Unmarshal failed at index %d %v", i, err)
+ }
+ if !got.Time.Equal(test.out.Time) {
+ t.Errorf("#%d: got %v, want %v", i, got.Time, test.out.Time)
+ }
+ }
+}
+
+type implicitTaggedTimeTest struct {
+ Time time.Time `asn1:"tag:24"`
+}
+
+func TestImplicitTaggedTime(t *testing.T) {
+ // An implicitly tagged time value, that happens to have an implicit
+ // tag equal to a GENERALIZEDTIME, should still be parsed as a UTCTime.
+ // (There's no "timeType" in fieldParameters to determine what type of
+ // time should be expected when implicitly tagged.)
+ der := []byte{0x30, 0x0f, 0x80 | 24, 0xd, '9', '1', '0', '5', '0', '6', '1', '6', '4', '5', '4', '0', 'Z'}
+ var result implicitTaggedTimeTest
+ if _, err := Unmarshal(der, &result); err != nil {
+ t.Fatalf("Error while parsing: %s", err)
+ }
+ if expected := time.Date(1991, 05, 06, 16, 45, 40, 0, time.UTC); !result.Time.Equal(expected) {
+ t.Errorf("Wrong result. Got %v, want %v", result.Time, expected)
+ }
+}
diff --git a/src/encoding/gob/encode.go b/src/encoding/gob/encode.go
index b7bf8b002..04a85410c 100644
--- a/src/encoding/gob/encode.go
+++ b/src/encoding/gob/encode.go
@@ -281,15 +281,16 @@ func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, value refle
field := value.FieldByIndex(instr.index)
if instr.indir > 0 {
field = encIndirect(field, instr.indir)
- }
- if !valid(field) {
- continue
+ // TODO: Is field guaranteed valid? If so we could avoid this check.
+ if !valid(field) {
+ continue
+ }
}
instr.op(instr, state, field)
}
}
-// encodeArray encodes the array whose 0th element is at p.
+// encodeArray encodes an array.
func (enc *Encoder) encodeArray(b *bytes.Buffer, value reflect.Value, op encOp, elemIndir int, length int) {
state := enc.newEncoderState(b)
defer enc.freeEncoderState(state)
@@ -300,6 +301,7 @@ func (enc *Encoder) encodeArray(b *bytes.Buffer, value reflect.Value, op encOp,
elem := value.Index(i)
if elemIndir > 0 {
elem = encIndirect(elem, elemIndir)
+ // TODO: Is elem guaranteed valid? If so we could avoid this check.
if !valid(elem) {
errorf("encodeArray: nil element")
}
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go
index 67ec37388..705bc2e17 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -173,7 +173,6 @@ type decodeState struct {
scan scanner
nextscan scanner // for calls to nextValue
savedError error
- tempstr string // scratch space to avoid some allocations
useNumber bool
}
@@ -293,6 +292,32 @@ func (d *decodeState) value(v reflect.Value) {
}
}
+type unquotedValue struct{}
+
+// valueQuoted is like value but decodes a
+// quoted string literal or literal null into an interface value.
+// If it finds anything other than a quoted string literal or null,
+// valueQuoted returns unquotedValue{}.
+func (d *decodeState) valueQuoted() interface{} {
+ switch op := d.scanWhile(scanSkipSpace); op {
+ default:
+ d.error(errPhase)
+
+ case scanBeginArray:
+ d.array(reflect.Value{})
+
+ case scanBeginObject:
+ d.object(reflect.Value{})
+
+ case scanBeginLiteral:
+ switch v := d.literalInterface().(type) {
+ case nil, string:
+ return v
+ }
+ }
+ return unquotedValue{}
+}
+
// indirect walks down v allocating pointers as needed,
// until it gets to a non-pointer.
// if it encounters an Unmarshaler, indirect stops and returns that.
@@ -444,6 +469,8 @@ func (d *decodeState) array(v reflect.Value) {
}
}
+var nullLiteral = []byte("null")
+
// object consumes an object from d.data[d.off-1:], decoding into the value v.
// the first byte ('{') of the object has been read already.
func (d *decodeState) object(v reflect.Value) {
@@ -566,9 +593,14 @@ func (d *decodeState) object(v reflect.Value) {
// Read value.
if destring {
- d.value(reflect.ValueOf(&d.tempstr))
- d.literalStore([]byte(d.tempstr), subv, true)
- d.tempstr = "" // Zero scratch space for successive values.
+ switch qv := d.valueQuoted().(type) {
+ case nil:
+ d.literalStore(nullLiteral, subv, false)
+ case string:
+ d.literalStore([]byte(qv), subv, true)
+ default:
+ d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into %v", item, v.Type()))
+ }
} else {
d.value(subv)
}
diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go
index d95657d72..7235969b9 100644
--- a/src/encoding/json/decode_test.go
+++ b/src/encoding/json/decode_test.go
@@ -1070,18 +1070,25 @@ func TestEmptyString(t *testing.T) {
}
}
-// Test that the returned error is non-nil when trying to unmarshal null string into int, for successive ,string option
-// Issue 7046
+// Test that a null for ,string is not replaced with the previous quoted string (issue 7046).
+// It should also not be an error (issue 2540, issue 8587).
func TestNullString(t *testing.T) {
type T struct {
- A int `json:",string"`
- B int `json:",string"`
+ A int `json:",string"`
+ B int `json:",string"`
+ C *int `json:",string"`
}
- data := []byte(`{"A": "1", "B": null}`)
+ data := []byte(`{"A": "1", "B": null, "C": null}`)
var s T
+ s.B = 1
+ s.C = new(int)
+ *s.C = 2
err := Unmarshal(data, &s)
- if err == nil {
- t.Fatalf("expected error; got %v", s)
+ if err != nil {
+ t.Fatalf("Unmarshal: %v")
+ }
+ if s.B != 1 || s.C != nil {
+ t.Fatalf("after Unmarshal, s.B=%d, s.C=%p, want 1, nil", s.B, s.C)
}
}
diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go
index b63538c92..9b7b9d5fd 100644
--- a/src/encoding/json/encode.go
+++ b/src/encoding/json/encode.go
@@ -93,6 +93,8 @@ import (
// as described in the next paragraph.
// An anonymous struct field with a name given in its JSON tag is treated as
// having that name, rather than being anonymous.
+// An anonymous struct field of interface type is treated the same as having
+// that type as its name, rather than being anonymous.
//
// The Go visibility rules for struct fields are amended for JSON when
// deciding which field to marshal or unmarshal. If there are
diff --git a/src/go/build/build.go b/src/go/build/build.go
index 5e11c9b9c..7a51cf3c0 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -417,6 +417,19 @@ func (e *NoGoError) Error() string {
return "no buildable Go source files in " + e.Dir
}
+// MultiplePackageError describes a directory containing
+// multiple buildable Go source files for multiple packages.
+type MultiplePackageError struct {
+ Dir string // directory containing files
+ Packages []string // package names found
+ Files []string // corresponding files: Files[i] declares package Packages[i]
+}
+
+func (e *MultiplePackageError) Error() string {
+ // Error string limited to two entries for compatibility.
+ return fmt.Sprintf("found packages %s (%s) and %s (%s) in %s", e.Packages[0], e.Files[0], e.Packages[1], e.Files[1], e.Dir)
+}
+
func nameExt(name string) string {
i := strings.LastIndex(name, ".")
if i < 0 {
@@ -675,7 +688,7 @@ Found:
p.Name = pkg
firstFile = name
} else if pkg != p.Name {
- return p, fmt.Errorf("found packages %s (%s) and %s (%s) in %s", p.Name, firstFile, pkg, name, p.Dir)
+ return p, &MultiplePackageError{p.Dir, []string{firstFile, name}, []string{p.Name, pkg}}
}
if pf.Doc != nil && p.Doc == "" {
p.Doc = doc.Synopsis(pf.Doc.Text())
@@ -1291,6 +1304,18 @@ func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
if dot := strings.Index(name, "."); dot != -1 {
name = name[:dot]
}
+
+ // Before Go 1.4, a file called "linux.go" would be equivalent to having a
+ // build tag "linux" in that file. For Go 1.4 and beyond, we require this
+ // auto-tagging to apply only to files with a non-empty prefix, so
+ // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
+ // sytems, such as android, to arrive without breaking existing code with
+ // innocuous source code in "android.go". The easiest fix: files without
+ // underscores are always included.
+ if !strings.ContainsRune(name, '_') {
+ return true
+ }
+
l := strings.Split(name, "_")
if n := len(l); n > 0 && l[n-1] == "test" {
l = l[:n-1]
diff --git a/src/go/build/build_test.go b/src/go/build/build_test.go
index 004010113..43d09cbd1 100644
--- a/src/go/build/build_test.go
+++ b/src/go/build/build_test.go
@@ -85,6 +85,20 @@ func TestEmptyImport(t *testing.T) {
}
}
+func TestEmptyFolderImport(t *testing.T) {
+ _, err := Import(".", "testdata/empty", 0)
+ if _, ok := err.(*NoGoError); !ok {
+ t.Fatal(`Import("testdata/empty") did not return NoGoError.`)
+ }
+}
+
+func TestMultiplePackageImport(t *testing.T) {
+ _, err := Import(".", "testdata/multi", 0)
+ if _, ok := err.(*MultiplePackageError); !ok {
+ t.Fatal(`Import("testdata/multi") did not return MultiplePackageError.`)
+ }
+}
+
func TestLocalDirectory(t *testing.T) {
cwd, err := os.Getwd()
if err != nil {
@@ -173,6 +187,10 @@ var matchFileTests = []struct {
{ctxtAndroid, "foo_linux.go", "", true},
{ctxtAndroid, "foo_android.go", "", true},
{ctxtAndroid, "foo_plan9.go", "", false},
+ {ctxtAndroid, "android.go", "", true},
+ {ctxtAndroid, "plan9.go", "", true},
+ {ctxtAndroid, "arm.s", "", true},
+ {ctxtAndroid, "amd64.s", "", true},
}
func TestMatchFile(t *testing.T) {
diff --git a/src/go/build/doc.go b/src/go/build/doc.go
index 56878f2b4..75a827bb9 100644
--- a/src/go/build/doc.go
+++ b/src/go/build/doc.go
@@ -108,12 +108,10 @@
// *_GOOS
// *_GOARCH
// *_GOOS_GOARCH
-// (example: source_windows_amd64.go) or the literals:
-// GOOS
-// GOARCH
-// (example: windows.go) where GOOS and GOARCH represent any known operating
-// system and architecture values respectively, then the file is considered to
-// have an implicit build constraint requiring those terms.
+// (example: source_windows_amd64.go) where GOOS and GOARCH represent
+// any known operating system and architecture values respectively, then
+// the file is considered to have an implicit build constraint requiring
+// those terms.
//
// To keep a file from being considered for the build:
//
diff --git a/src/go/build/testdata/empty/dummy b/src/go/build/testdata/empty/dummy
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/go/build/testdata/empty/dummy
diff --git a/src/go/build/testdata/multi/file.go b/src/go/build/testdata/multi/file.go
new file mode 100644
index 000000000..ee946eb2a
--- /dev/null
+++ b/src/go/build/testdata/multi/file.go
@@ -0,0 +1,5 @@
+// Test data - not compiled.
+
+package main
+
+func main() {}
diff --git a/src/go/build/testdata/multi/file_appengine.go b/src/go/build/testdata/multi/file_appengine.go
new file mode 100644
index 000000000..4ea31e703
--- /dev/null
+++ b/src/go/build/testdata/multi/file_appengine.go
@@ -0,0 +1,5 @@
+// Test data - not compiled.
+
+package test_package
+
+func init() {}
diff --git a/src/liblink/data.c b/src/liblink/data.c
index 4504f4171..e5efa2eb2 100644
--- a/src/liblink/data.c
+++ b/src/liblink/data.c
@@ -83,6 +83,8 @@ savedata(Link *ctxt, LSym *s, Prog *p, char *pn)
siz = ctxt->arch->datasize(p);
if(off < 0 || siz < 0 || off >= 1<<30 || siz >= 100)
mangle(pn);
+ if(ctxt->enforce_data_order && off < s->np)
+ ctxt->diag("data out of order (already have %d)\n%P", p);
symgrow(ctxt, s, off+siz);
if(p->to.type == ctxt->arch->D_FCONST) {
diff --git a/src/math/big/int.go b/src/math/big/int.go
index 3998652e9..d22e39e7c 100644
--- a/src/math/big/int.go
+++ b/src/math/big/int.go
@@ -752,15 +752,16 @@ func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int {
return z
}
-// ModInverse sets z to the multiplicative inverse of g in the group ℤ/pℤ (where
-// p is a prime) and returns z.
-func (z *Int) ModInverse(g, p *Int) *Int {
+// ModInverse sets z to the multiplicative inverse of g in the ring ℤ/nℤ
+// and returns z. If g and n are not relatively prime, the result is undefined.
+func (z *Int) ModInverse(g, n *Int) *Int {
var d Int
- d.GCD(z, nil, g, p)
- // x and y are such that g*x + p*y = d. Since p is prime, d = 1. Taking
- // that modulo p results in g*x = 1, therefore x is the inverse element.
+ d.GCD(z, nil, g, n)
+ // x and y are such that g*x + n*y = d. Since g and n are
+ // relatively prime, d = 1. Taking that modulo n results in
+ // g*x = 1, therefore x is the inverse element.
if z.neg {
- z.Add(z, p)
+ z.Add(z, n)
}
return z
}
@@ -1016,12 +1017,12 @@ func (z *Int) UnmarshalJSON(text []byte) error {
return nil
}
-// MarshalText implements the encoding.TextMarshaler interface
+// MarshalText implements the encoding.TextMarshaler interface.
func (z *Int) MarshalText() (text []byte, err error) {
return []byte(z.String()), nil
}
-// UnmarshalText implements the encoding.TextUnmarshaler interface
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
func (z *Int) UnmarshalText(text []byte) error {
if _, ok := z.SetString(string(text), 0); !ok {
return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Int", text)
diff --git a/src/math/big/int_test.go b/src/math/big/int_test.go
index ec05fbb1c..6070cf325 100644
--- a/src/math/big/int_test.go
+++ b/src/math/big/int_test.go
@@ -1448,24 +1448,40 @@ func TestNot(t *testing.T) {
var modInverseTests = []struct {
element string
- prime string
+ modulus string
}{
- {"1", "7"},
- {"1", "13"},
+ {"1234567", "458948883992"},
{"239487239847", "2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919"},
}
func TestModInverse(t *testing.T) {
- var element, prime Int
+ var element, modulus, gcd, inverse Int
one := NewInt(1)
for i, test := range modInverseTests {
(&element).SetString(test.element, 10)
- (&prime).SetString(test.prime, 10)
- inverse := new(Int).ModInverse(&element, &prime)
- inverse.Mul(inverse, &element)
- inverse.Mod(inverse, &prime)
- if inverse.Cmp(one) != 0 {
- t.Errorf("#%d: failed (e·e^(-1)=%s)", i, inverse)
+ (&modulus).SetString(test.modulus, 10)
+ (&inverse).ModInverse(&element, &modulus)
+ (&inverse).Mul(&inverse, &element)
+ (&inverse).Mod(&inverse, &modulus)
+ if (&inverse).Cmp(one) != 0 {
+ t.Errorf("#%d: failed (e·e^(-1)=%s)", i, &inverse)
+ }
+ }
+ // exhaustive test for small values
+ for n := 2; n < 100; n++ {
+ (&modulus).SetInt64(int64(n))
+ for x := 1; x < n; x++ {
+ (&element).SetInt64(int64(x))
+ (&gcd).GCD(nil, nil, &element, &modulus)
+ if (&gcd).Cmp(one) != 0 {
+ continue
+ }
+ (&inverse).ModInverse(&element, &modulus)
+ (&inverse).Mul(&inverse, &element)
+ (&inverse).Mod(&inverse, &modulus)
+ if (&inverse).Cmp(one) != 0 {
+ t.Errorf("ModInverse(%d,%d)*%d%%%d=%d, not 1", &element, &modulus, &element, &modulus, &inverse)
+ }
}
}
}
diff --git a/src/math/big/rat.go b/src/math/big/rat.go
index e6ab0bb48..c5339fe44 100644
--- a/src/math/big/rat.go
+++ b/src/math/big/rat.go
@@ -552,6 +552,9 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
if z.b.abs, _, err = z.b.abs.scan(strings.NewReader(s), 10); err != nil {
return nil, false
}
+ if len(z.b.abs) == 0 {
+ return nil, false
+ }
return z.norm(), true
}
@@ -699,12 +702,12 @@ func (z *Rat) GobDecode(buf []byte) error {
return nil
}
-// MarshalText implements the encoding.TextMarshaler interface
+// MarshalText implements the encoding.TextMarshaler interface.
func (r *Rat) MarshalText() (text []byte, err error) {
return []byte(r.RatString()), nil
}
-// UnmarshalText implements the encoding.TextUnmarshaler interface
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
func (r *Rat) UnmarshalText(text []byte) error {
if _, ok := r.SetString(string(text)); !ok {
return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Rat", text)
diff --git a/src/math/big/rat_test.go b/src/math/big/rat_test.go
index 598eac8cc..5dbbb3510 100644
--- a/src/math/big/rat_test.go
+++ b/src/math/big/rat_test.go
@@ -89,6 +89,7 @@ var setStringTests = []struct {
{"53/70893980658822810696", "53/70893980658822810696", true},
{"106/141787961317645621392", "53/70893980658822810696", true},
{"204211327800791583.81095", "4084226556015831676219/20000", true},
+ {in: "1/0", ok: false},
}
func TestRatSetString(t *testing.T) {
diff --git a/src/net/fd_windows.go b/src/net/fd_windows.go
index 6d69e0624..f3a534a1d 100644
--- a/src/net/fd_windows.go
+++ b/src/net/fd_windows.go
@@ -294,6 +294,18 @@ func (fd *netFD) init() error {
fd.skipSyncNotif = true
}
}
+ // Disable SIO_UDP_CONNRESET behavior.
+ // http://support.microsoft.com/kb/263823
+ switch fd.net {
+ case "udp", "udp4", "udp6":
+ ret := uint32(0)
+ flag := uint32(0)
+ size := uint32(unsafe.Sizeof(flag))
+ err := syscall.WSAIoctl(fd.sysfd, syscall.SIO_UDP_CONNRESET, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, nil, 0)
+ if err != nil {
+ return os.NewSyscallError("WSAIoctl", err)
+ }
+ }
fd.rop.mode = 'r'
fd.wop.mode = 'w'
fd.rop.fd = fd
diff --git a/src/net/http/client.go b/src/net/http/client.go
index a5a3abe61..ce884d1f0 100644
--- a/src/net/http/client.go
+++ b/src/net/http/client.go
@@ -101,6 +101,30 @@ type RoundTripper interface {
// return true if the string includes a port.
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
+// refererForURL returns a referer without any authentication info or
+// an empty string if lastReq scheme is https and newReq scheme is http.
+func refererForURL(lastReq, newReq *url.URL) string {
+ // https://tools.ietf.org/html/rfc7231#section-5.5.2
+ // "Clients SHOULD NOT include a Referer header field in a
+ // (non-secure) HTTP request if the referring page was
+ // transferred with a secure protocol."
+ if lastReq.Scheme == "https" && newReq.Scheme == "http" {
+ return ""
+ }
+ referer := lastReq.String()
+ if lastReq.User != nil {
+ // This is not very efficient, but is the best we can
+ // do without:
+ // - introducing a new method on URL
+ // - creating a race condition
+ // - copying the URL struct manually, which would cause
+ // maintenance problems down the line
+ auth := lastReq.User.String() + "@"
+ referer = strings.Replace(referer, auth, "", 1)
+ }
+ return referer
+}
+
// Used in Send to implement io.ReadCloser by bundling together the
// bufio.Reader through which we read the response, and the underlying
// network connection.
@@ -324,8 +348,8 @@ func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bo
if len(via) > 0 {
// Add the Referer header.
lastReq := via[len(via)-1]
- if lastReq.URL.Scheme != "https" {
- nreq.Header.Set("Referer", lastReq.URL.String())
+ if ref := refererForURL(lastReq.URL, nreq.URL); ref != "" {
+ nreq.Header.Set("Referer", ref)
}
err = redirectChecker(nreq, via)
diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go
index 6392c1baf..56b6563c4 100644
--- a/src/net/http/client_test.go
+++ b/src/net/http/client_test.go
@@ -1036,3 +1036,40 @@ func TestClientTrailers(t *testing.T) {
t.Errorf("Response trailers = %#v; want %#v", res.Trailer, want)
}
}
+
+func TestReferer(t *testing.T) {
+ tests := []struct {
+ lastReq, newReq string // from -> to URLs
+ want string
+ }{
+ // don't send user:
+ {"http://gopher@test.com", "http://link.com", "http://test.com"},
+ {"https://gopher@test.com", "https://link.com", "https://test.com"},
+
+ // don't send a user and password:
+ {"http://gopher:go@test.com", "http://link.com", "http://test.com"},
+ {"https://gopher:go@test.com", "https://link.com", "https://test.com"},
+
+ // nothing to do:
+ {"http://test.com", "http://link.com", "http://test.com"},
+ {"https://test.com", "https://link.com", "https://test.com"},
+
+ // https to http doesn't send a referer:
+ {"https://test.com", "http://link.com", ""},
+ {"https://gopher:go@test.com", "http://link.com", ""},
+ }
+ for _, tt := range tests {
+ l, err := url.Parse(tt.lastReq)
+ if err != nil {
+ t.Fatal(err)
+ }
+ n, err := url.Parse(tt.newReq)
+ if err != nil {
+ t.Fatal(err)
+ }
+ r := ExportRefererForURL(l, n)
+ if r != tt.want {
+ t.Errorf("refererForURL(%q, %q) = %q; want %q", tt.lastReq, tt.newReq, r, tt.want)
+ }
+ }
+}
diff --git a/src/net/http/export_test.go b/src/net/http/export_test.go
index a6980b538..87b6c0773 100644
--- a/src/net/http/export_test.go
+++ b/src/net/http/export_test.go
@@ -9,6 +9,7 @@ package http
import (
"net"
+ "net/url"
"time"
)
@@ -92,6 +93,10 @@ func ResetCachedEnvironment() {
var DefaultUserAgent = defaultUserAgent
+func ExportRefererForURL(lastReq, newReq *url.URL) string {
+ return refererForURL(lastReq, newReq)
+}
+
// SetPendingDialHooks sets the hooks that run before and after handling
// pending dials.
func SetPendingDialHooks(before, after func()) {
diff --git a/src/net/http/response_test.go b/src/net/http/response_test.go
index 2dd0fad11..06e940d9a 100644
--- a/src/net/http/response_test.go
+++ b/src/net/http/response_test.go
@@ -377,6 +377,34 @@ some body`,
"Body here\n",
},
+
+ // 206 Partial Content. golang.org/issue/8923
+ {
+ "HTTP/1.1 206 Partial Content\r\n" +
+ "Content-Type: text/plain; charset=utf-8\r\n" +
+ "Accept-Ranges: bytes\r\n" +
+ "Content-Range: bytes 0-5/1862\r\n" +
+ "Content-Length: 6\r\n\r\n" +
+ "foobar",
+
+ Response{
+ Status: "206 Partial Content",
+ StatusCode: 206,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Request: dummyReq("GET"),
+ Header: Header{
+ "Accept-Ranges": []string{"bytes"},
+ "Content-Length": []string{"6"},
+ "Content-Type": []string{"text/plain; charset=utf-8"},
+ "Content-Range": []string{"bytes 0-5/1862"},
+ },
+ ContentLength: 6,
+ },
+
+ "foobar",
+ },
}
func TestReadResponse(t *testing.T) {
diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go
index 702bffdc1..bb44ac853 100644
--- a/src/net/http/serve_test.go
+++ b/src/net/http/serve_test.go
@@ -2659,6 +2659,103 @@ func TestCloseWrite(t *testing.T) {
}
}
+// This verifies that a handler can Flush and then Hijack.
+//
+// An similar test crashed once during development, but it was only
+// testing this tangentially and temporarily until another TODO was
+// fixed.
+//
+// So add an explicit test for this.
+func TestServerFlushAndHijack(t *testing.T) {
+ defer afterTest(t)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ io.WriteString(w, "Hello, ")
+ w.(Flusher).Flush()
+ conn, buf, _ := w.(Hijacker).Hijack()
+ buf.WriteString("6\r\nworld!\r\n0\r\n\r\n")
+ if err := buf.Flush(); err != nil {
+ t.Error(err)
+ }
+ if err := conn.Close(); err != nil {
+ t.Error(err)
+ }
+ }))
+ defer ts.Close()
+ res, err := Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer res.Body.Close()
+ all, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want := "Hello, world!"; string(all) != want {
+ t.Errorf("Got %q; want %q", all, want)
+ }
+}
+
+// golang.org/issue/8534 -- the Server shouldn't reuse a connection
+// for keep-alive after it's seen any Write error (e.g. a timeout) on
+// that net.Conn.
+//
+// To test, verify we don't timeout or see fewer unique client
+// addresses (== unique connections) than requests.
+func TestServerKeepAliveAfterWriteError(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in -short mode")
+ }
+ defer afterTest(t)
+ const numReq = 3
+ addrc := make(chan string, numReq)
+ ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ addrc <- r.RemoteAddr
+ time.Sleep(500 * time.Millisecond)
+ w.(Flusher).Flush()
+ }))
+ ts.Config.WriteTimeout = 250 * time.Millisecond
+ ts.Start()
+ defer ts.Close()
+
+ errc := make(chan error, numReq)
+ go func() {
+ defer close(errc)
+ for i := 0; i < numReq; i++ {
+ res, err := Get(ts.URL)
+ if res != nil {
+ res.Body.Close()
+ }
+ errc <- err
+ }
+ }()
+
+ timeout := time.NewTimer(numReq * 2 * time.Second) // 4x overkill
+ defer timeout.Stop()
+ addrSeen := map[string]bool{}
+ numOkay := 0
+ for {
+ select {
+ case v := <-addrc:
+ addrSeen[v] = true
+ case err, ok := <-errc:
+ if !ok {
+ if len(addrSeen) != numReq {
+ t.Errorf("saw %d unique client addresses; want %d", len(addrSeen), numReq)
+ }
+ if numOkay != 0 {
+ t.Errorf("got %d successful client requests; want 0", numOkay)
+ }
+ return
+ }
+ if err == nil {
+ numOkay++
+ }
+ case <-timeout.C:
+ t.Fatal("timeout waiting for requests to complete")
+ }
+ }
+}
+
func BenchmarkClientServer(b *testing.B) {
b.ReportAllocs()
b.StopTimer()
diff --git a/src/net/http/server.go b/src/net/http/server.go
index b5959f732..008d5aa7a 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -114,6 +114,8 @@ type conn struct {
remoteAddr string // network address of remote side
server *Server // the Server on which the connection arrived
rwc net.Conn // i/o connection
+ w io.Writer // checkConnErrorWriter's copy of wrc, not zeroed on Hijack
+ werr error // any errors writing to w
sr liveSwitchReader // where the LimitReader reads from; usually the rwc
lr *io.LimitedReader // io.LimitReader(sr)
buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->sr->rwc
@@ -432,13 +434,14 @@ func (srv *Server) newConn(rwc net.Conn) (c *conn, err error) {
c.remoteAddr = rwc.RemoteAddr().String()
c.server = srv
c.rwc = rwc
+ c.w = rwc
if debugServerConnections {
c.rwc = newLoggingConn("server", c.rwc)
}
c.sr = liveSwitchReader{r: c.rwc}
c.lr = io.LimitReader(&c.sr, noLimit).(*io.LimitedReader)
br := newBufioReader(c.lr)
- bw := newBufioWriterSize(c.rwc, 4<<10)
+ bw := newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
c.buf = bufio.NewReadWriter(br, bw)
return c, nil
}
@@ -956,8 +959,10 @@ func (w *response) bodyAllowed() bool {
// 2. (*response).w, a *bufio.Writer of bufferBeforeChunkingSize bytes
// 3. chunkWriter.Writer (whose writeHeader finalizes Content-Length/Type)
// and which writes the chunk headers, if needed.
-// 4. conn.buf, a bufio.Writer of default (4kB) bytes
-// 5. the rwc, the net.Conn.
+// 4. conn.buf, a bufio.Writer of default (4kB) bytes, writing to ->
+// 5. checkConnErrorWriter{c}, which notes any non-nil error on Write
+// and populates c.werr with it if so. but otherwise writes to:
+// 6. the rwc, the net.Conn.
//
// TODO(bradfitz): short-circuit some of the buffering when the
// initial header contains both a Content-Type and Content-Length.
@@ -1027,6 +1032,12 @@ func (w *response) finishRequest() {
// Did not write enough. Avoid getting out of sync.
w.closeAfterReply = true
}
+
+ // There was some error writing to the underlying connection
+ // during the request, so don't re-use this conn.
+ if w.conn.werr != nil {
+ w.closeAfterReply = true
+ }
}
func (w *response) Flush() {
@@ -2068,3 +2079,18 @@ func (c *loggingConn) Close() (err error) {
log.Printf("%s.Close() = %v", c.name, err)
return
}
+
+// checkConnErrorWriter writes to c.rwc and records any write errors to c.werr.
+// It only contains one field (and a pointer field at that), so it
+// fits in an interface value without an extra allocation.
+type checkConnErrorWriter struct {
+ c *conn
+}
+
+func (w checkConnErrorWriter) Write(p []byte) (n int, err error) {
+ n, err = w.c.w.Write(p) // c.w == c.rwc, except after a hijack, when rwc is nil.
+ if err != nil && w.c.werr == nil {
+ w.c.werr = err
+ }
+ return
+}
diff --git a/src/net/http/transport.go b/src/net/http/transport.go
index 70e574fc8..782f7cd39 100644
--- a/src/net/http/transport.go
+++ b/src/net/http/transport.go
@@ -1040,11 +1040,14 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
}
// Ask for a compressed version if the caller didn't set their
- // own value for Accept-Encoding. We only attempted to
+ // own value for Accept-Encoding. We only attempt to
// uncompress the gzip stream if we were the layer that
// requested it.
requestedGzip := false
- if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" && req.Method != "HEAD" {
+ if !pc.t.DisableCompression &&
+ req.Header.Get("Accept-Encoding") == "" &&
+ req.Header.Get("Range") == "" &&
+ req.Method != "HEAD" {
// Request gzip only, not deflate. Deflate is ambiguous and
// not as universally supported anyway.
// See: http://www.gzip.org/zlib/zlib_faq.html#faq38
@@ -1053,6 +1056,10 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
// due to a bug in nginx:
// http://trac.nginx.org/nginx/ticket/358
// http://golang.org/issue/5522
+ //
+ // We don't request gzip if the request is for a range, since
+ // auto-decoding a portion of a gzipped document will just fail
+ // anyway. See http://golang.org/issue/8923
requestedGzip = true
req.extraHeaders().Set("Accept-Encoding", "gzip")
}
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index 66fcc3c7d..defa63370 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -2216,6 +2216,39 @@ func TestTransportCloseIdleConnsThenReturn(t *testing.T) {
wantIdle("after final put", 1)
}
+// This tests that an client requesting a content range won't also
+// implicitly ask for gzip support. If they want that, they need to do it
+// on their own.
+// golang.org/issue/8923
+func TestTransportRangeAndGzip(t *testing.T) {
+ defer afterTest(t)
+ reqc := make(chan *Request, 1)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ reqc <- r
+ }))
+ defer ts.Close()
+
+ req, _ := NewRequest("GET", ts.URL, nil)
+ req.Header.Set("Range", "bytes=7-11")
+ res, err := DefaultClient.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ select {
+ case r := <-reqc:
+ if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
+ t.Error("Transport advertised gzip support in the Accept header")
+ }
+ if r.Header.Get("Range") == "" {
+ t.Error("no Range in request")
+ }
+ case <-time.After(10 * time.Second):
+ t.Fatal("timeout")
+ }
+ res.Body.Close()
+}
+
func wantBody(res *http.Response, err error, want string) error {
if err != nil {
return err
diff --git a/src/net/rpc/client.go b/src/net/rpc/client.go
index 21f79b068..d0c4a6921 100644
--- a/src/net/rpc/client.go
+++ b/src/net/rpc/client.go
@@ -41,10 +41,10 @@ type Call struct {
type Client struct {
codec ClientCodec
- sending sync.Mutex
+ reqMutex sync.Mutex // protects following
+ request Request
mutex sync.Mutex // protects following
- request Request
seq uint64
pending map[uint64]*Call
closing bool // user has called Close
@@ -69,8 +69,8 @@ type ClientCodec interface {
}
func (client *Client) send(call *Call) {
- client.sending.Lock()
- defer client.sending.Unlock()
+ client.reqMutex.Lock()
+ defer client.reqMutex.Unlock()
// Register this call.
client.mutex.Lock()
@@ -146,7 +146,7 @@ func (client *Client) input() {
}
}
// Terminate pending calls.
- client.sending.Lock()
+ client.reqMutex.Lock()
client.mutex.Lock()
client.shutdown = true
closing := client.closing
@@ -162,7 +162,7 @@ func (client *Client) input() {
call.done()
}
client.mutex.Unlock()
- client.sending.Unlock()
+ client.reqMutex.Unlock()
if debugLog && err != io.EOF && !closing {
log.Println("rpc: client protocol error:", err)
}
diff --git a/src/net/rpc/client_test.go b/src/net/rpc/client_test.go
index bbfc1ec3a..5dd111b29 100644
--- a/src/net/rpc/client_test.go
+++ b/src/net/rpc/client_test.go
@@ -6,6 +6,10 @@ package rpc
import (
"errors"
+ "fmt"
+ "net"
+ "runtime"
+ "strings"
"testing"
)
@@ -34,3 +38,54 @@ func TestCloseCodec(t *testing.T) {
t.Error("client.Close did not close codec")
}
}
+
+// Test that errors in gob shut down the connection. Issue 7689.
+
+type R struct {
+ msg []byte // Not exported, so R does not work with gob.
+}
+
+type S struct{}
+
+func (s *S) Recv(nul *struct{}, reply *R) error {
+ *reply = R{[]byte("foo")}
+ return nil
+}
+
+func TestGobError(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping test; see http://golang.org/issue/8908")
+ }
+ defer func() {
+ err := recover()
+ if err == nil {
+ t.Fatal("no error")
+ }
+ if !strings.Contains("reading body EOF", err.(error).Error()) {
+ t.Fatal("expected `reading body EOF', got", err)
+ }
+ }()
+ Register(new(S))
+
+ listen, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ panic(err)
+ }
+ go Accept(listen)
+
+ client, err := Dial("tcp", listen.Addr().String())
+ if err != nil {
+ panic(err)
+ }
+
+ var reply Reply
+ err = client.Call("S.Recv", &struct{}{}, &reply)
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Printf("%#v\n", reply)
+ client.Close()
+
+ listen.Close()
+}
diff --git a/src/net/udp_test.go b/src/net/udp_test.go
index e1778779c..125bbca6c 100644
--- a/src/net/udp_test.go
+++ b/src/net/udp_test.go
@@ -9,6 +9,7 @@ import (
"runtime"
"strings"
"testing"
+ "time"
)
func TestResolveUDPAddr(t *testing.T) {
@@ -34,6 +35,46 @@ func TestResolveUDPAddr(t *testing.T) {
}
}
+func TestReadFromUDP(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9":
+ t.Skipf("skipping test on %q, see issue 8916", runtime.GOOS)
+ }
+
+ ra, err := ResolveUDPAddr("udp", "127.0.0.1:7")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ la, err := ResolveUDPAddr("udp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ c, err := ListenUDP("udp", la)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ _, err = c.WriteToUDP([]byte("a"), ra)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = c.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ if err != nil {
+ t.Fatal(err)
+ }
+ b := make([]byte, 1)
+ _, _, err = c.ReadFromUDP(b)
+ if err == nil {
+ t.Fatal("ReadFromUDP should fail")
+ } else if !isTimeout(err) {
+ t.Fatal(err)
+ }
+}
+
func TestWriteToUDP(t *testing.T) {
switch runtime.GOOS {
case "plan9":
diff --git a/src/net/url/url.go b/src/net/url/url.go
index 0b32cd7c8..f167408fa 100644
--- a/src/net/url/url.go
+++ b/src/net/url/url.go
@@ -441,6 +441,24 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) {
}
// String reassembles the URL into a valid URL string.
+// The general form of the result is one of:
+//
+// scheme:opaque
+// scheme://userinfo@host/path?query#fragment
+//
+// If u.Opaque is non-empty, String uses the first form;
+// otherwise it uses the second form.
+//
+// In the second form, the following rules apply:
+// - if u.Scheme is empty, scheme: is omitted.
+// - if u.User is nil, userinfo@ is omitted.
+// - if u.Host is empty, host/ is omitted.
+// - if u.Scheme and u.Host are empty and u.User is nil,
+// the entire scheme://userinfo@host/ is omitted.
+// - if u.Host is non-empty and u.Path begins with a /,
+// the form host/path does not add its own /.
+// - if u.RawQuery is empty, ?query is omitted.
+// - if u.Fragment is empty, #fragment is omitted.
func (u *URL) String() string {
var buf bytes.Buffer
if u.Scheme != "" {
diff --git a/src/os/error_plan9.go b/src/os/error_plan9.go
index 45cd74792..001cdfcf2 100644
--- a/src/os/error_plan9.go
+++ b/src/os/error_plan9.go
@@ -25,7 +25,8 @@ func isNotExist(err error) bool {
case *LinkError:
err = pe.Err
}
- return contains(err.Error(), "does not exist") || contains(err.Error(), "not found") || contains(err.Error(), "has been removed")
+ return contains(err.Error(), "does not exist") || contains(err.Error(), "not found") ||
+ contains(err.Error(), "has been removed") || contains(err.Error(), "no parent")
}
func isPermission(err error) bool {
diff --git a/src/os/exec/exec.go b/src/os/exec/exec.go
index 4aded4171..72b4905d5 100644
--- a/src/os/exec/exec.go
+++ b/src/os/exec/exec.go
@@ -55,8 +55,15 @@ type Cmd struct {
// calling process's current directory.
Dir string
- // Stdin specifies the process's standard input. If Stdin is
- // nil, the process reads from the null device (os.DevNull).
+ // Stdin specifies the process's standard input.
+ // If Stdin is nil, the process reads from the null device (os.DevNull).
+ // If Stdin is an *os.File, the process's standard input is connected
+ // directly to that file.
+ // Otherwise, during the execution of the command a separate
+ // goroutine reads from Stdin and delivers that data to the command
+ // over a pipe. In this case, Wait does not complete until the goroutine
+ // stops copying, either because it has reached the end of Stdin
+ // (EOF or a read error) or because writing to the pipe returned an error.
Stdin io.Reader
// Stdout and Stderr specify the process's standard output and error.
diff --git a/src/os/exec_unix.go b/src/os/exec_unix.go
index 1b1e3350b..ed97f85e2 100644
--- a/src/os/exec_unix.go
+++ b/src/os/exec_unix.go
@@ -34,18 +34,26 @@ func (p *Process) wait() (ps *ProcessState, err error) {
return ps, nil
}
+var errFinished = errors.New("os: process already finished")
+
func (p *Process) signal(sig Signal) error {
- if p.done() {
- return errors.New("os: process already finished")
- }
if p.Pid == -1 {
return errors.New("os: process already released")
}
+ if p.Pid == 0 {
+ return errors.New("os: process not initialized")
+ }
+ if p.done() {
+ return errFinished
+ }
s, ok := sig.(syscall.Signal)
if !ok {
return errors.New("os: unsupported signal type")
}
if e := syscall.Kill(p.Pid, s); e != nil {
+ if e == syscall.ESRCH {
+ return errFinished
+ }
return e
}
return nil
diff --git a/src/os/exec_windows.go b/src/os/exec_windows.go
index c4f3d4f85..393393b23 100644
--- a/src/os/exec_windows.go
+++ b/src/os/exec_windows.go
@@ -53,6 +53,9 @@ func terminateProcess(pid, exitcode int) error {
}
func (p *Process) signal(sig Signal) error {
+ if p.handle == uintptr(syscall.InvalidHandle) {
+ return syscall.EINVAL
+ }
if p.done() {
return errors.New("os: process already finished")
}
diff --git a/src/os/os_test.go b/src/os/os_test.go
index 973cc3a7b..a30a2b031 100644
--- a/src/os/os_test.go
+++ b/src/os/os_test.go
@@ -124,7 +124,22 @@ func newFile(testName string, t *testing.T) (f *File) {
}
f, err := ioutil.TempFile(dir, "_Go_"+testName)
if err != nil {
- t.Fatalf("open %s: %s", testName, err)
+ t.Fatalf("TempFile %s: %s", testName, err)
+ }
+ return
+}
+
+func newDir(testName string, t *testing.T) (name string) {
+ // Use a local file system, not NFS.
+ // On Unix, override $TMPDIR in case the user
+ // has it set to an NFS-mounted directory.
+ dir := ""
+ if runtime.GOOS != "android" && runtime.GOOS != "windows" {
+ dir = "/tmp"
+ }
+ name, err := ioutil.TempDir(dir, "_Go_"+testName)
+ if err != nil {
+ t.Fatalf("TempDir %s: %s", testName, err)
}
return
}
@@ -755,35 +770,49 @@ func TestTruncate(t *testing.T) {
}
}
-// Use TempDir() to make sure we're on a local file system,
+// Use TempDir (via newFile) to make sure we're on a local file system,
// so that timings are not distorted by latency and caching.
// On NFS, timings can be off due to caching of meta-data on
// NFS servers (Issue 848).
func TestChtimes(t *testing.T) {
f := newFile("TestChtimes", t)
defer Remove(f.Name())
- defer f.Close()
f.Write([]byte("hello, world\n"))
f.Close()
- st, err := Stat(f.Name())
+ testChtimes(t, f.Name())
+}
+
+// Use TempDir (via newDir) to make sure we're on a local file system,
+// so that timings are not distorted by latency and caching.
+// On NFS, timings can be off due to caching of meta-data on
+// NFS servers (Issue 848).
+func TestChtimesDir(t *testing.T) {
+ name := newDir("TestChtimes", t)
+ defer RemoveAll(name)
+
+ testChtimes(t, name)
+}
+
+func testChtimes(t *testing.T, name string) {
+ st, err := Stat(name)
if err != nil {
- t.Fatalf("Stat %s: %s", f.Name(), err)
+ t.Fatalf("Stat %s: %s", name, err)
}
preStat := st
// Move access and modification time back a second
at := Atime(preStat)
mt := preStat.ModTime()
- err = Chtimes(f.Name(), at.Add(-time.Second), mt.Add(-time.Second))
+ err = Chtimes(name, at.Add(-time.Second), mt.Add(-time.Second))
if err != nil {
- t.Fatalf("Chtimes %s: %s", f.Name(), err)
+ t.Fatalf("Chtimes %s: %s", name, err)
}
- st, err = Stat(f.Name())
+ st, err = Stat(name)
if err != nil {
- t.Fatalf("second Stat %s: %s", f.Name(), err)
+ t.Fatalf("second Stat %s: %s", name, err)
}
postStat := st
diff --git a/src/os/path.go b/src/os/path.go
index 24a3415b4..84a3be334 100644
--- a/src/os/path.go
+++ b/src/os/path.go
@@ -17,7 +17,7 @@ import (
// If path is already a directory, MkdirAll does nothing
// and returns nil.
func MkdirAll(path string, perm FileMode) error {
- // If path exists, stop with success or error.
+ // Fast path: if we can tell whether path is a directory or file, stop with success or error.
dir, err := Stat(path)
if err == nil {
if dir.IsDir() {
@@ -26,7 +26,7 @@ func MkdirAll(path string, perm FileMode) error {
return &PathError{"mkdir", path, syscall.ENOTDIR}
}
- // Doesn't already exist; make sure parent does.
+ // Slow path: make sure parent exists and then call Mkdir for path.
i := len(path)
for i > 0 && IsPathSeparator(path[i-1]) { // Skip trailing path separator.
i--
@@ -45,7 +45,7 @@ func MkdirAll(path string, perm FileMode) error {
}
}
- // Now parent exists, try to create.
+ // Parent now exists; invoke Mkdir and use its result.
err = Mkdir(path, perm)
if err != nil {
// Handle arguments like "foo/." by
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index d17ef5c5e..6bdc9be9d 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -1543,7 +1543,17 @@ func TestMakeFuncVariadic(t *testing.T) {
fv := MakeFunc(TypeOf(fn), func(in []Value) []Value { return in[1:2] })
ValueOf(&fn).Elem().Set(fv)
- r := fv.Call([]Value{ValueOf(1), ValueOf(2), ValueOf(3)})[0].Interface().([]int)
+ r := fn(1, 2, 3)
+ if r[0] != 2 || r[1] != 3 {
+ t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1])
+ }
+
+ r = fn(1, []int{2, 3}...)
+ if r[0] != 2 || r[1] != 3 {
+ t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1])
+ }
+
+ r = fv.Call([]Value{ValueOf(1), ValueOf(2), ValueOf(3)})[0].Interface().([]int)
if r[0] != 2 || r[1] != 3 {
t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1])
}
@@ -1552,6 +1562,17 @@ func TestMakeFuncVariadic(t *testing.T) {
if r[0] != 2 || r[1] != 3 {
t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1])
}
+
+ f := fv.Interface().(func(int, ...int) []int)
+
+ r = f(1, 2, 3)
+ if r[0] != 2 || r[1] != 3 {
+ t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1])
+ }
+ r = f(1, []int{2, 3}...)
+ if r[0] != 2 || r[1] != 3 {
+ t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1])
+ }
}
type Point struct {
@@ -1569,6 +1590,24 @@ func (p Point) Dist(scale int) int {
return p.x*p.x*scale + p.y*p.y*scale
}
+// This will be index 2.
+func (p Point) GCMethod(k int) int {
+ runtime.GC()
+ return k + p.x
+}
+
+// This will be index 3.
+func (p Point) TotalDist(points ...Point) int {
+ tot := 0
+ for _, q := range points {
+ dx := q.x - p.x
+ dy := q.y - p.y
+ tot += dx*dx + dy*dy // Should call Sqrt, but it's just a test.
+
+ }
+ return tot
+}
+
func TestMethod(t *testing.T) {
// Non-curried method of type.
p := Point{3, 4}
@@ -1751,6 +1790,37 @@ func TestMethodValue(t *testing.T) {
}
}
+func TestVariadicMethodValue(t *testing.T) {
+ p := Point{3, 4}
+ points := []Point{{20, 21}, {22, 23}, {24, 25}}
+ want := int64(p.TotalDist(points[0], points[1], points[2]))
+
+ // Curried method of value.
+ tfunc := TypeOf((func(...Point) int)(nil))
+ v := ValueOf(p).Method(3)
+ if tt := v.Type(); tt != tfunc {
+ t.Errorf("Variadic Method Type is %s; want %s", tt, tfunc)
+ }
+ i := ValueOf(v.Interface()).Call([]Value{ValueOf(points[0]), ValueOf(points[1]), ValueOf(points[2])})[0].Int()
+ if i != want {
+ t.Errorf("Variadic Method returned %d; want %d", i, want)
+ }
+ i = ValueOf(v.Interface()).CallSlice([]Value{ValueOf(points)})[0].Int()
+ if i != want {
+ t.Errorf("Variadic Method CallSlice returned %d; want %d", i, want)
+ }
+
+ f := v.Interface().(func(...Point) int)
+ i = int64(f(points[0], points[1], points[2]))
+ if i != want {
+ t.Errorf("Variadic Method Interface returned %d; want %d", i, want)
+ }
+ i = int64(f(points...))
+ if i != want {
+ t.Errorf("Variadic Method Interface Slice returned %d; want %d", i, want)
+ }
+}
+
// Reflect version of $GOROOT/test/method5.go
// Concrete types implementing M method.
@@ -3770,11 +3840,6 @@ func TestReflectFuncTraceback(t *testing.T) {
f.Call([]Value{})
}
-func (p Point) GCMethod(k int) int {
- runtime.GC()
- return k + p.x
-}
-
func TestReflectMethodTraceback(t *testing.T) {
p := Point{3, 4}
m := ValueOf(p).MethodByName("GCMethod")
@@ -3953,3 +4018,9 @@ func TestInvalid(t *testing.T) {
t.Errorf("field elem: IsValid=%v, Kind=%v, want false, Invalid", v.IsValid(), v.Kind())
}
}
+
+// Issue 8917.
+func TestLargeGCProg(t *testing.T) {
+ fv := ValueOf(func([256]*byte) {})
+ fv.Call([]Value{ValueOf([256]*byte{})})
+}
diff --git a/src/reflect/makefunc.go b/src/reflect/makefunc.go
index bdb8c21d7..1072c7fab 100644
--- a/src/reflect/makefunc.go
+++ b/src/reflect/makefunc.go
@@ -60,7 +60,7 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
impl := &makeFuncImpl{code: code, stack: stack, typ: ftyp, fn: fn}
- return Value{t, unsafe.Pointer(impl), 0, flag(Func) << flagKindShift}
+ return Value{t, unsafe.Pointer(impl), flag(Func) << flagKindShift}
}
// makeFuncStub is an assembly function that is the code half of
@@ -92,7 +92,7 @@ func makeMethodValue(op string, v Value) Value {
// Ignoring the flagMethod bit, v describes the receiver, not the method type.
fl := v.flag & (flagRO | flagAddr | flagIndir)
fl |= flag(v.typ.Kind()) << flagKindShift
- rcvr := Value{v.typ, v.ptr, v.scalar, fl}
+ rcvr := Value{v.typ, v.ptr, fl}
// v.Type returns the actual type of the method value.
funcType := v.Type().(*rtype)
@@ -118,7 +118,7 @@ func makeMethodValue(op string, v Value) Value {
// but we want Interface() and other operations to fail early.
methodReceiver(op, fv.rcvr, fv.method)
- return Value{funcType, unsafe.Pointer(fv), 0, v.flag&flagRO | flag(Func)<<flagKindShift}
+ return Value{funcType, unsafe.Pointer(fv), v.flag&flagRO | flag(Func)<<flagKindShift}
}
// methodValueCall is an assembly function that is the code half of
diff --git a/src/reflect/type.go b/src/reflect/type.go
index f099546d2..26328e74b 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -498,7 +498,7 @@ func (t *uncommonType) Method(i int) (m Method) {
mt := p.typ
m.Type = mt
fn := unsafe.Pointer(&p.tfn)
- m.Func = Value{mt, fn, 0, fl}
+ m.Func = Value{mt, fn, fl}
m.Index = i
return
}
@@ -1514,20 +1514,32 @@ func (gc *gcProg) appendProg(t *rtype) {
gc.size += t.size
return
}
- nptr := t.size / unsafe.Sizeof(uintptr(0))
- var prog []byte
- if t.kind&kindGCProg != 0 {
- // Ensure that the runtime has unrolled GC program.
- // TODO(rsc): Do not allocate.
- unsafe_New(t)
- // The program is stored in t.gc[0], skip unroll flag.
- prog = (*[1 << 30]byte)(unsafe.Pointer(t.gc[0]))[1:]
- } else {
- // The mask is embed directly in t.gc.
- prog = (*[1 << 30]byte)(unsafe.Pointer(&t.gc[0]))[:]
- }
- for i := uintptr(0); i < nptr; i++ {
- gc.appendWord(extractGCWord(prog, i))
+ switch t.Kind() {
+ default:
+ panic("reflect: non-pointer type marked as having pointers")
+ case Ptr, UnsafePointer, Chan, Func, Map:
+ gc.appendWord(bitsPointer)
+ case Slice:
+ gc.appendWord(bitsPointer)
+ gc.appendWord(bitsScalar)
+ gc.appendWord(bitsScalar)
+ case String:
+ gc.appendWord(bitsPointer)
+ gc.appendWord(bitsScalar)
+ case Array:
+ c := t.Len()
+ e := t.Elem().common()
+ for i := 0; i < c; i++ {
+ gc.appendProg(e)
+ }
+ case Interface:
+ gc.appendWord(bitsPointer)
+ gc.appendWord(bitsPointer)
+ case Struct:
+ c := t.NumField()
+ for i := 0; i < c; i++ {
+ gc.appendProg(t.Field(i).Type.common())
+ }
}
}
@@ -1562,7 +1574,6 @@ func (gc *gcProg) finalize() unsafe.Pointer {
gc.appendWord(extractGCWord(gc.gc, i))
}
}
- gc.gc = append([]byte{1}, gc.gc...) // prepend unroll flag
return unsafe.Pointer(&gc.gc[0])
}
@@ -1574,9 +1585,14 @@ func (gc *gcProg) align(a uintptr) {
gc.size = align(gc.size, a)
}
+// These constants must stay in sync with ../runtime/mgc0.h.
const (
- bitsScalar = 1
- bitsPointer = 2
+ bitsScalar = 1
+ bitsPointer = 2
+ bitsMultiWord = 3
+
+ bitsIface = 2
+ bitsEface = 3
)
// Make sure these routines stay in sync with ../../runtime/hashmap.go!
@@ -1619,7 +1635,6 @@ func bucketOf(ktyp, etyp *rtype) *rtype {
b := new(rtype)
b.size = gc.size
b.gc[0] = gc.finalize()
- b.kind |= kindGCProg
s := "bucket(" + *ktyp.string + "," + *etyp.string + ")"
b.string = &s
return b
@@ -1786,7 +1801,7 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
// Reflect uses the "interface" calling convention for
// methods, where receivers take one word of argument
// space no matter how big they actually are.
- if !isDirectIface(rcvr) {
+ if ifaceIndir(rcvr) {
// we pass a pointer to the receiver.
gc.append(bitsPointer)
stack.append2(bitsPointer)
@@ -1821,7 +1836,6 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
x := new(rtype)
x.size = gc.size
x.gc[0] = gc.finalize()
- x.kind |= kindGCProg
var s string
if rcvr != nil {
s = "methodargs(" + *rcvr.string + ")(" + *t.string + ")"
@@ -1844,9 +1858,9 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
return x, argSize, retOffset, stack
}
-// isDirectIface reports whether t is stored directly in an interface value.
-func isDirectIface(t *rtype) bool {
- return t.kind&kindDirectIface != 0
+// ifaceIndir reports whether t is stored indirectly in an interface value.
+func ifaceIndir(t *rtype) bool {
+ return t.kind&kindDirectIface == 0
}
// Layout matches runtime.BitVector (well enough).
diff --git a/src/reflect/value.go b/src/reflect/value.go
index 9c65ee270..c6e8038eb 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -7,11 +7,9 @@ package reflect
import (
"math"
"runtime"
- "strconv"
"unsafe"
)
-const bigEndian = false // can be smarter if we find a big-endian machine
const ptrSize = unsafe.Sizeof((*byte)(nil))
const cannotSet = "cannot set value obtained from unexported struct field"
@@ -40,12 +38,6 @@ type Value struct {
// Valid when either flagIndir is set or typ.pointers() is true.
ptr unsafe.Pointer
- // Non-pointer-valued data. When the data is smaller
- // than a word, it begins at the first byte (in the memory
- // address sense) of this field.
- // Valid when flagIndir is not set and typ.pointers() is false.
- scalar uintptr
-
// flag holds metadata about the value.
// The lowest bits are flag bits:
// - flagRO: obtained via unexported field, so read-only
@@ -56,7 +48,7 @@ type Value struct {
// This repeats typ.Kind() except for method values.
// The remaining 23+ bits give a method number for method values.
// If flag.kind() != Func, code can assume that flagMethod is unset.
- // If !isDirectIface(typ), code can assume that flagIndir is set.
+ // If ifaceIndir(typ), code can assume that flagIndir is set.
flag
// A method value represents a curried method invocation
@@ -102,7 +94,7 @@ func packEface(v Value) interface{} {
e := (*emptyInterface)(unsafe.Pointer(&i))
// First, fill in the data portion of the interface.
switch {
- case !isDirectIface(t):
+ case ifaceIndir(t):
if v.flag&flagIndir == 0 {
panic("bad indir")
}
@@ -119,18 +111,10 @@ func packEface(v Value) interface{} {
case v.flag&flagIndir != 0:
// Value is indirect, but interface is direct. We need
// to load the data at v.ptr into the interface data word.
- if t.pointers() {
- e.word = iword(*(*unsafe.Pointer)(v.ptr))
- } else {
- e.word = iword(loadScalar(v.ptr, t.size))
- }
+ e.word = iword(*(*unsafe.Pointer)(v.ptr))
default:
// Value is direct, and so is the interface.
- if t.pointers() {
- e.word = iword(v.ptr)
- } else {
- e.word = iword(v.scalar)
- }
+ e.word = iword(v.ptr)
}
// Now, fill in the type portion. We're very careful here not
// to have any operation between the e.word and e.typ assignments
@@ -149,13 +133,10 @@ func unpackEface(i interface{}) Value {
return Value{}
}
f := flag(t.Kind()) << flagKindShift
- if !isDirectIface(t) {
- return Value{t, unsafe.Pointer(e.word), 0, f | flagIndir}
- }
- if t.pointers() {
- return Value{t, unsafe.Pointer(e.word), 0, f}
+ if ifaceIndir(t) {
+ f |= flagIndir
}
- return Value{t, nil, uintptr(e.word), f}
+ return Value{t, unsafe.Pointer(e.word), f}
}
// A ValueError occurs when a Value method is invoked on
@@ -194,64 +175,6 @@ func methodName() string {
// to the GC here so that GC remains precise.
type iword unsafe.Pointer
-// loadScalar loads n bytes at p from memory into a uintptr
-// that forms the second word of an interface. The data
-// must be non-pointer in nature.
-func loadScalar(p unsafe.Pointer, n uintptr) uintptr {
- // Run the copy ourselves instead of calling memmove
- // to avoid moving w to the heap.
- var w uintptr
- switch n {
- default:
- panic("reflect: internal error: loadScalar of " + strconv.Itoa(int(n)) + "-byte value")
- case 0:
- case 1:
- *(*uint8)(unsafe.Pointer(&w)) = *(*uint8)(p)
- case 2:
- *(*uint16)(unsafe.Pointer(&w)) = *(*uint16)(p)
- case 3:
- *(*[3]byte)(unsafe.Pointer(&w)) = *(*[3]byte)(p)
- case 4:
- *(*uint32)(unsafe.Pointer(&w)) = *(*uint32)(p)
- case 5:
- *(*[5]byte)(unsafe.Pointer(&w)) = *(*[5]byte)(p)
- case 6:
- *(*[6]byte)(unsafe.Pointer(&w)) = *(*[6]byte)(p)
- case 7:
- *(*[7]byte)(unsafe.Pointer(&w)) = *(*[7]byte)(p)
- case 8:
- *(*uint64)(unsafe.Pointer(&w)) = *(*uint64)(p)
- }
- return w
-}
-
-// storeScalar stores n bytes from w into p.
-func storeScalar(p unsafe.Pointer, w uintptr, n uintptr) {
- // Run the copy ourselves instead of calling memmove
- // to avoid moving w to the heap.
- switch n {
- default:
- panic("reflect: internal error: storeScalar of " + strconv.Itoa(int(n)) + "-byte value")
- case 0:
- case 1:
- *(*uint8)(p) = *(*uint8)(unsafe.Pointer(&w))
- case 2:
- *(*uint16)(p) = *(*uint16)(unsafe.Pointer(&w))
- case 3:
- *(*[3]byte)(p) = *(*[3]byte)(unsafe.Pointer(&w))
- case 4:
- *(*uint32)(p) = *(*uint32)(unsafe.Pointer(&w))
- case 5:
- *(*[5]byte)(p) = *(*[5]byte)(unsafe.Pointer(&w))
- case 6:
- *(*[6]byte)(p) = *(*[6]byte)(unsafe.Pointer(&w))
- case 7:
- *(*[7]byte)(p) = *(*[7]byte)(unsafe.Pointer(&w))
- case 8:
- *(*uint64)(p) = *(*uint64)(unsafe.Pointer(&w))
- }
-}
-
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ *rtype
@@ -321,17 +244,14 @@ func (v Value) Addr() Value {
if v.flag&flagAddr == 0 {
panic("reflect.Value.Addr of unaddressable value")
}
- return Value{v.typ.ptrTo(), v.ptr, 0, (v.flag & flagRO) | flag(Ptr)<<flagKindShift}
+ return Value{v.typ.ptrTo(), v.ptr, (v.flag & flagRO) | flag(Ptr)<<flagKindShift}
}
// Bool returns v's underlying value.
// It panics if v's kind is not Bool.
func (v Value) Bool() bool {
v.mustBe(Bool)
- if v.flag&flagIndir != 0 {
- return *(*bool)(v.ptr)
- }
- return *(*bool)(unsafe.Pointer(&v.scalar))
+ return *(*bool)(v.ptr)
}
// Bytes returns v's underlying value.
@@ -501,10 +421,8 @@ func (v Value) call(op string, in []Value) []Value {
v = v.assignTo("reflect.Value.Call", targ, (*interface{})(addr))
if v.flag&flagIndir != 0 {
memmove(addr, v.ptr, n)
- } else if targ.pointers() {
- *(*unsafe.Pointer)(addr) = v.ptr
} else {
- storeScalar(addr, v.scalar, n)
+ *(*unsafe.Pointer)(addr) = v.ptr
}
off += n
}
@@ -525,7 +443,7 @@ func (v Value) call(op string, in []Value) []Value {
a := uintptr(tv.Align())
off = (off + a - 1) &^ (a - 1)
fl := flagIndir | flag(tv.Kind())<<flagKindShift
- ret[i] = Value{tv.common(), unsafe.Pointer(uintptr(args) + off), 0, fl}
+ ret[i] = Value{tv.common(), unsafe.Pointer(uintptr(args) + off), fl}
off += tv.Size()
}
@@ -556,8 +474,8 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) {
typ := arg
off += -off & uintptr(typ.align-1)
addr := unsafe.Pointer(uintptr(ptr) + off)
- v := Value{typ, nil, 0, flag(typ.Kind()) << flagKindShift}
- if !isDirectIface(typ) {
+ v := Value{typ, nil, flag(typ.Kind()) << flagKindShift}
+ if ifaceIndir(typ) {
// value cannot be inlined in interface data.
// Must make a copy, because f might keep a reference to it,
// and we cannot let f keep a reference to the stack frame
@@ -565,10 +483,8 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) {
v.ptr = unsafe_New(typ)
memmove(v.ptr, addr, typ.size)
v.flag |= flagIndir
- } else if typ.pointers() {
- v.ptr = *(*unsafe.Pointer)(addr)
} else {
- v.scalar = loadScalar(addr, typ.size)
+ v.ptr = *(*unsafe.Pointer)(addr)
}
in = append(in, v)
off += typ.size
@@ -602,10 +518,8 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) {
addr := unsafe.Pointer(uintptr(ptr) + off)
if v.flag&flagIndir != 0 {
memmove(addr, v.ptr, typ.size)
- } else if typ.pointers() {
- *(*unsafe.Pointer)(addr) = v.ptr
} else {
- storeScalar(addr, v.scalar, typ.size)
+ *(*unsafe.Pointer)(addr) = v.ptr
}
off += typ.size
}
@@ -663,18 +577,10 @@ func storeRcvr(v Value, p unsafe.Pointer) {
// the interface data word becomes the receiver word
iface := (*nonEmptyInterface)(v.ptr)
*(*unsafe.Pointer)(p) = unsafe.Pointer(iface.word)
- } else if v.flag&flagIndir != 0 {
- if !isDirectIface(t) {
- *(*unsafe.Pointer)(p) = v.ptr
- } else if t.pointers() {
- *(*unsafe.Pointer)(p) = *(*unsafe.Pointer)(v.ptr)
- } else {
- *(*uintptr)(p) = loadScalar(v.ptr, t.size)
- }
- } else if t.pointers() {
- *(*unsafe.Pointer)(p) = v.ptr
+ } else if v.flag&flagIndir != 0 && !ifaceIndir(t) {
+ *(*unsafe.Pointer)(p) = *(*unsafe.Pointer)(v.ptr)
} else {
- *(*uintptr)(p) = v.scalar
+ *(*unsafe.Pointer)(p) = v.ptr
}
}
@@ -763,12 +669,8 @@ func (v Value) Complex() complex128 {
k := v.kind()
switch k {
case Complex64:
- if v.flag&flagIndir != 0 {
- return complex128(*(*complex64)(v.ptr))
- }
- return complex128(*(*complex64)(unsafe.Pointer(&v.scalar)))
+ return complex128(*(*complex64)(v.ptr))
case Complex128:
- // complex128 is always bigger than a word; assume flagIndir.
return *(*complex128)(v.ptr)
}
panic(&ValueError{"reflect.Value.Complex", k})
@@ -808,7 +710,7 @@ func (v Value) Elem() Value {
typ := tt.elem
fl := v.flag&flagRO | flagIndir | flagAddr
fl |= flag(typ.Kind() << flagKindShift)
- return Value{typ, ptr, 0, fl}
+ return Value{typ, ptr, fl}
}
panic(&ValueError{"reflect.Value.Elem", k})
}
@@ -833,30 +735,25 @@ func (v Value) Field(i int) Value {
fl |= flag(typ.Kind()) << flagKindShift
var ptr unsafe.Pointer
- var scalar uintptr
- switch {
- case fl&flagIndir != 0:
+ if fl&flagIndir != 0 {
// Indirect. Just bump pointer.
ptr = unsafe.Pointer(uintptr(v.ptr) + field.offset)
- case typ.pointers():
+ } else {
if field.offset != 0 {
panic("field access of ptr value isn't at offset 0")
}
ptr = v.ptr
- case bigEndian:
- // Must be scalar. Discard leading bytes.
- scalar = v.scalar << (field.offset * 8)
- default:
- // Must be scalar. Discard leading bytes.
- scalar = v.scalar >> (field.offset * 8)
}
- return Value{typ, ptr, scalar, fl}
+ return Value{typ, ptr, fl}
}
// FieldByIndex returns the nested field corresponding to index.
// It panics if v's Kind is not struct.
func (v Value) FieldByIndex(index []int) Value {
+ if len(index) == 1 {
+ return v.Field(index[0])
+ }
v.mustBe(Struct)
for i, x := range index {
if i > 0 {
@@ -901,15 +798,9 @@ func (v Value) Float() float64 {
k := v.kind()
switch k {
case Float32:
- if v.flag&flagIndir != 0 {
- return float64(*(*float32)(v.ptr))
- }
- return float64(*(*float32)(unsafe.Pointer(&v.scalar)))
+ return float64(*(*float32)(v.ptr))
case Float64:
- if v.flag&flagIndir != 0 {
- return *(*float64)(v.ptr)
- }
- return *(*float64)(unsafe.Pointer(&v.scalar))
+ return *(*float64)(v.ptr)
}
panic(&ValueError{"reflect.Value.Float", k})
}
@@ -932,12 +823,10 @@ func (v Value) Index(i int) Value {
offset := uintptr(i) * typ.size
var val unsafe.Pointer
- var scalar uintptr
- switch {
- case fl&flagIndir != 0:
+ if fl&flagIndir != 0 {
// Indirect. Just bump pointer.
val = unsafe.Pointer(uintptr(v.ptr) + offset)
- case typ.pointers():
+ } else {
if offset != 0 {
// This is an array stored inline in an interface value.
// And the array element type has pointers.
@@ -948,14 +837,8 @@ func (v Value) Index(i int) Value {
panic("reflect: internal error: unexpected array index")
}
val = v.ptr
- case bigEndian:
- // Direct. Discard leading bytes.
- scalar = v.scalar << (offset * 8)
- default:
- // Direct. Discard leading bytes.
- scalar = v.scalar >> (offset * 8)
}
- return Value{typ, val, scalar, fl}
+ return Value{typ, val, fl}
case Slice:
// Element flag same as Elem of Ptr.
@@ -969,7 +852,7 @@ func (v Value) Index(i int) Value {
typ := tt.elem
fl |= flag(typ.Kind()) << flagKindShift
val := unsafe.Pointer(uintptr(s.Data) + uintptr(i)*typ.size)
- return Value{typ, val, 0, fl}
+ return Value{typ, val, fl}
case String:
fl := v.flag&flagRO | flag(Uint8<<flagKindShift) | flagIndir
@@ -978,7 +861,7 @@ func (v Value) Index(i int) Value {
panic("reflect: string index out of range")
}
p := unsafe.Pointer(uintptr(s.Data) + uintptr(i))
- return Value{uint8Type, p, 0, fl}
+ return Value{uint8Type, p, fl}
}
panic(&ValueError{"reflect.Value.Index", k})
}
@@ -987,14 +870,7 @@ func (v Value) Index(i int) Value {
// It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64.
func (v Value) Int() int64 {
k := v.kind()
- var p unsafe.Pointer
- if v.flag&flagIndir != 0 {
- p = v.ptr
- } else {
- // The escape analysis is good enough that &v.scalar
- // does not trigger a heap allocation.
- p = unsafe.Pointer(&v.scalar)
- }
+ p := v.ptr
switch k {
case Int:
return int64(*(*int)(p))
@@ -1154,10 +1030,8 @@ func (v Value) MapIndex(key Value) Value {
var k unsafe.Pointer
if key.flag&flagIndir != 0 {
k = key.ptr
- } else if key.typ.pointers() {
- k = unsafe.Pointer(&key.ptr)
} else {
- k = unsafe.Pointer(&key.scalar)
+ k = unsafe.Pointer(&key.ptr)
}
e := mapaccess(v.typ, v.pointer(), k)
if e == nil {
@@ -1166,16 +1040,14 @@ func (v Value) MapIndex(key Value) Value {
typ := tt.elem
fl := (v.flag | key.flag) & flagRO
fl |= flag(typ.Kind()) << flagKindShift
- if !isDirectIface(typ) {
+ if ifaceIndir(typ) {
// Copy result so future changes to the map
// won't change the underlying value.
c := unsafe_New(typ)
memmove(c, e, typ.size)
- return Value{typ, c, 0, fl | flagIndir}
- } else if typ.pointers() {
- return Value{typ, *(*unsafe.Pointer)(e), 0, fl}
+ return Value{typ, c, fl | flagIndir}
} else {
- return Value{typ, nil, loadScalar(e, typ.size), fl}
+ return Value{typ, *(*unsafe.Pointer)(e), fl}
}
}
@@ -1206,16 +1078,14 @@ func (v Value) MapKeys() []Value {
// we can do about it.
break
}
- if !isDirectIface(keyType) {
+ if ifaceIndir(keyType) {
// Copy result so future changes to the map
// won't change the underlying value.
c := unsafe_New(keyType)
memmove(c, key, keyType.size)
- a[i] = Value{keyType, c, 0, fl | flagIndir}
- } else if keyType.pointers() {
- a[i] = Value{keyType, *(*unsafe.Pointer)(key), 0, fl}
+ a[i] = Value{keyType, c, fl | flagIndir}
} else {
- a[i] = Value{keyType, nil, loadScalar(key, keyType.size), fl}
+ a[i] = Value{keyType, *(*unsafe.Pointer)(key), fl}
}
mapiternext(it)
}
@@ -1239,7 +1109,7 @@ func (v Value) Method(i int) Value {
fl := v.flag & (flagRO | flagIndir)
fl |= flag(Func) << flagKindShift
fl |= flag(i)<<flagMethodShift | flagMethod
- return Value{v.typ, v.ptr, v.scalar, fl}
+ return Value{v.typ, v.ptr, fl}
}
// NumMethod returns the number of methods in the value's method set.
@@ -1403,16 +1273,14 @@ func (v Value) recv(nb bool) (val Value, ok bool) {
panic("reflect: recv on send-only channel")
}
t := tt.elem
- val = Value{t, nil, 0, flag(t.Kind()) << flagKindShift}
+ val = Value{t, nil, flag(t.Kind()) << flagKindShift}
var p unsafe.Pointer
- if !isDirectIface(t) {
+ if ifaceIndir(t) {
p = unsafe_New(t)
val.ptr = p
val.flag |= flagIndir
- } else if t.pointers() {
- p = unsafe.Pointer(&val.ptr)
} else {
- p = unsafe.Pointer(&val.scalar)
+ p = unsafe.Pointer(&val.ptr)
}
selected, ok := chanrecv(v.typ, v.pointer(), nb, p)
if !selected {
@@ -1442,10 +1310,8 @@ func (v Value) send(x Value, nb bool) (selected bool) {
var p unsafe.Pointer
if x.flag&flagIndir != 0 {
p = x.ptr
- } else if x.typ.pointers() {
- p = unsafe.Pointer(&x.ptr)
} else {
- p = unsafe.Pointer(&x.scalar)
+ p = unsafe.Pointer(&x.ptr)
}
return chansend(v.typ, v.pointer(), p, nb)
}
@@ -1463,10 +1329,8 @@ func (v Value) Set(x Value) {
x = x.assignTo("reflect.Set", v.typ, target)
if x.flag&flagIndir != 0 {
memmove(v.ptr, x.ptr, v.typ.size)
- } else if x.typ.pointers() {
- *(*unsafe.Pointer)(v.ptr) = x.ptr
} else {
- memmove(v.ptr, unsafe.Pointer(&x.scalar), v.typ.size)
+ *(*unsafe.Pointer)(v.ptr) = x.ptr
}
}
@@ -1589,10 +1453,8 @@ func (v Value) SetMapIndex(key, val Value) {
var k unsafe.Pointer
if key.flag&flagIndir != 0 {
k = key.ptr
- } else if key.typ.pointers() {
- k = unsafe.Pointer(&key.ptr)
} else {
- k = unsafe.Pointer(&key.scalar)
+ k = unsafe.Pointer(&key.ptr)
}
if val.typ == nil {
mapdelete(v.typ, v.pointer(), k)
@@ -1603,10 +1465,8 @@ func (v Value) SetMapIndex(key, val Value) {
var e unsafe.Pointer
if val.flag&flagIndir != 0 {
e = val.ptr
- } else if val.typ.pointers() {
- e = unsafe.Pointer(&val.ptr)
} else {
- e = unsafe.Pointer(&val.scalar)
+ e = unsafe.Pointer(&val.ptr)
}
mapassign(v.typ, v.pointer(), k, e)
}
@@ -1683,7 +1543,7 @@ func (v Value) Slice(i, j int) Value {
panic("reflect.Value.Slice: string slice index out of bounds")
}
t := stringHeader{unsafe.Pointer(uintptr(s.Data) + uintptr(i)), j - i}
- return Value{v.typ, unsafe.Pointer(&t), 0, v.flag}
+ return Value{v.typ, unsafe.Pointer(&t), v.flag}
}
if i < 0 || j < i || j > cap {
@@ -1705,7 +1565,7 @@ func (v Value) Slice(i, j int) Value {
}
fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift
- return Value{typ.common(), unsafe.Pointer(&x), 0, fl}
+ return Value{typ.common(), unsafe.Pointer(&x), fl}
}
// Slice3 is the 3-index form of the slice operation: it returns v[i:j:k].
@@ -1757,7 +1617,7 @@ func (v Value) Slice3(i, j, k int) Value {
}
fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift
- return Value{typ.common(), unsafe.Pointer(&x), 0, fl}
+ return Value{typ.common(), unsafe.Pointer(&x), fl}
}
// String returns the string v's underlying value, as a string.
@@ -1833,14 +1693,7 @@ func (v Value) Type() Type {
// It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64.
func (v Value) Uint() uint64 {
k := v.kind()
- var p unsafe.Pointer
- if v.flag&flagIndir != 0 {
- p = v.ptr
- } else {
- // The escape analysis is good enough that &v.scalar
- // does not trigger a heap allocation.
- p = unsafe.Pointer(&v.scalar)
- }
+ p := v.ptr
switch k {
case Uint:
return uint64(*(*uint)(p))
@@ -1994,17 +1847,6 @@ func Copy(dst, src Value) int {
n = sn
}
- // If sk is an in-line array, cannot take its address.
- // Instead, copy element by element.
- // TODO: memmove would be ok for this (sa = unsafe.Pointer(&v.scalar))
- // if we teach the compiler that ptrs don't escape from memmove.
- if src.flag&flagIndir == 0 {
- for i := 0; i < n; i++ {
- dst.Index(i).Set(src.Index(i))
- }
- return n
- }
-
// Copy via memmove.
var da, sa unsafe.Pointer
if dk == Array {
@@ -2012,7 +1854,9 @@ func Copy(dst, src Value) int {
} else {
da = (*sliceHeader)(dst.ptr).Data
}
- if sk == Array {
+ if src.flag&flagIndir == 0 {
+ sa = unsafe.Pointer(&src.ptr)
+ } else if sk == Array {
sa = src.ptr
} else {
sa = (*sliceHeader)(src.ptr).Data
@@ -2125,10 +1969,8 @@ func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) {
v = v.assignTo("reflect.Select", tt.elem, nil)
if v.flag&flagIndir != 0 {
rc.val = v.ptr
- } else if v.typ.pointers() {
- rc.val = unsafe.Pointer(&v.ptr)
} else {
- rc.val = unsafe.Pointer(&v.scalar)
+ rc.val = unsafe.Pointer(&v.ptr)
}
case SelectRecv:
@@ -2157,12 +1999,10 @@ func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) {
t := tt.elem
p := runcases[chosen].val
fl := flag(t.Kind()) << flagKindShift
- if !isDirectIface(t) {
- recv = Value{t, p, 0, fl | flagIndir}
- } else if t.pointers() {
- recv = Value{t, *(*unsafe.Pointer)(p), 0, fl}
+ if ifaceIndir(t) {
+ recv = Value{t, p, fl | flagIndir}
} else {
- recv = Value{t, nil, loadScalar(p, t.size), fl}
+ recv = Value{t, *(*unsafe.Pointer)(p), fl}
}
}
return chosen, recv, recvOK
@@ -2193,7 +2033,7 @@ func MakeSlice(typ Type, len, cap int) Value {
}
s := sliceHeader{unsafe_NewArray(typ.Elem().(*rtype), cap), len, cap}
- return Value{typ.common(), unsafe.Pointer(&s), 0, flagIndir | flag(Slice)<<flagKindShift}
+ return Value{typ.common(), unsafe.Pointer(&s), flagIndir | flag(Slice)<<flagKindShift}
}
// MakeChan creates a new channel with the specified type and buffer size.
@@ -2208,7 +2048,7 @@ func MakeChan(typ Type, buffer int) Value {
panic("reflect.MakeChan: unidirectional channel type")
}
ch := makechan(typ.(*rtype), uint64(buffer))
- return Value{typ.common(), ch, 0, flag(Chan) << flagKindShift}
+ return Value{typ.common(), ch, flag(Chan) << flagKindShift}
}
// MakeMap creates a new map of the specified type.
@@ -2217,7 +2057,7 @@ func MakeMap(typ Type) Value {
panic("reflect.MakeMap of non-map type")
}
m := makemap(typ.(*rtype))
- return Value{typ.common(), m, 0, flag(Map) << flagKindShift}
+ return Value{typ.common(), m, flag(Map) << flagKindShift}
}
// Indirect returns the value that v points to.
@@ -2258,10 +2098,10 @@ func Zero(typ Type) Value {
}
t := typ.common()
fl := flag(t.Kind()) << flagKindShift
- if isDirectIface(t) {
- return Value{t, nil, 0, fl}
+ if ifaceIndir(t) {
+ return Value{t, unsafe_New(typ.(*rtype)), fl | flagIndir}
}
- return Value{t, unsafe_New(typ.(*rtype)), 0, fl | flagIndir}
+ return Value{t, nil, fl}
}
// New returns a Value representing a pointer to a new zero value
@@ -2272,14 +2112,14 @@ func New(typ Type) Value {
}
ptr := unsafe_New(typ.(*rtype))
fl := flag(Ptr) << flagKindShift
- return Value{typ.common().ptrTo(), ptr, 0, fl}
+ return Value{typ.common().ptrTo(), ptr, fl}
}
// NewAt returns a Value representing a pointer to a value of the
// specified type, using p as that pointer.
func NewAt(typ Type, p unsafe.Pointer) Value {
fl := flag(Ptr) << flagKindShift
- return Value{typ.common().ptrTo(), p, 0, fl}
+ return Value{typ.common().ptrTo(), p, fl}
}
// assignTo returns a value v that can be assigned directly to typ.
@@ -2297,7 +2137,7 @@ func (v Value) assignTo(context string, dst *rtype, target *interface{}) Value {
v.typ = dst
fl := v.flag & (flagRO | flagAddr | flagIndir)
fl |= flag(dst.Kind()) << flagKindShift
- return Value{dst, v.ptr, v.scalar, fl}
+ return Value{dst, v.ptr, fl}
case implements(dst, v.typ):
if target == nil {
@@ -2309,7 +2149,7 @@ func (v Value) assignTo(context string, dst *rtype, target *interface{}) Value {
} else {
ifaceE2I(dst, x, unsafe.Pointer(target))
}
- return Value{dst, unsafe.Pointer(target), 0, flagIndir | flag(Interface)<<flagKindShift}
+ return Value{dst, unsafe.Pointer(target), flagIndir | flag(Interface)<<flagKindShift}
}
// Failed.
@@ -2417,82 +2257,46 @@ func convertOp(dst, src *rtype) func(Value, Type) Value {
// where t is a signed or unsigned int type.
func makeInt(f flag, bits uint64, t Type) Value {
typ := t.common()
- if !isDirectIface(typ) {
- ptr := unsafe_New(typ)
- switch typ.size {
- case 1:
- *(*uint8)(unsafe.Pointer(ptr)) = uint8(bits)
- case 2:
- *(*uint16)(unsafe.Pointer(ptr)) = uint16(bits)
- case 4:
- *(*uint32)(unsafe.Pointer(ptr)) = uint32(bits)
- case 8:
- *(*uint64)(unsafe.Pointer(ptr)) = bits
- }
- return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<<flagKindShift}
- }
- var s uintptr
+ ptr := unsafe_New(typ)
switch typ.size {
case 1:
- *(*uint8)(unsafe.Pointer(&s)) = uint8(bits)
+ *(*uint8)(unsafe.Pointer(ptr)) = uint8(bits)
case 2:
- *(*uint16)(unsafe.Pointer(&s)) = uint16(bits)
+ *(*uint16)(unsafe.Pointer(ptr)) = uint16(bits)
case 4:
- *(*uint32)(unsafe.Pointer(&s)) = uint32(bits)
+ *(*uint32)(unsafe.Pointer(ptr)) = uint32(bits)
case 8:
- *(*uint64)(unsafe.Pointer(&s)) = uint64(bits)
+ *(*uint64)(unsafe.Pointer(ptr)) = bits
}
- return Value{typ, nil, s, f | flag(typ.Kind())<<flagKindShift}
+ return Value{typ, ptr, f | flagIndir | flag(typ.Kind())<<flagKindShift}
}
// makeFloat returns a Value of type t equal to v (possibly truncated to float32),
// where t is a float32 or float64 type.
func makeFloat(f flag, v float64, t Type) Value {
typ := t.common()
- if !isDirectIface(typ) {
- ptr := unsafe_New(typ)
- switch typ.size {
- case 4:
- *(*float32)(unsafe.Pointer(ptr)) = float32(v)
- case 8:
- *(*float64)(unsafe.Pointer(ptr)) = v
- }
- return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<<flagKindShift}
- }
-
- var s uintptr
+ ptr := unsafe_New(typ)
switch typ.size {
case 4:
- *(*float32)(unsafe.Pointer(&s)) = float32(v)
+ *(*float32)(unsafe.Pointer(ptr)) = float32(v)
case 8:
- *(*float64)(unsafe.Pointer(&s)) = v
+ *(*float64)(unsafe.Pointer(ptr)) = v
}
- return Value{typ, nil, s, f | flag(typ.Kind())<<flagKindShift}
+ return Value{typ, ptr, f | flagIndir | flag(typ.Kind())<<flagKindShift}
}
// makeComplex returns a Value of type t equal to v (possibly truncated to complex64),
// where t is a complex64 or complex128 type.
func makeComplex(f flag, v complex128, t Type) Value {
typ := t.common()
- if !isDirectIface(typ) {
- ptr := unsafe_New(typ)
- switch typ.size {
- case 8:
- *(*complex64)(unsafe.Pointer(ptr)) = complex64(v)
- case 16:
- *(*complex128)(unsafe.Pointer(ptr)) = v
- }
- return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<<flagKindShift}
- }
-
- var s uintptr
+ ptr := unsafe_New(typ)
switch typ.size {
case 8:
- *(*complex64)(unsafe.Pointer(&s)) = complex64(v)
+ *(*complex64)(unsafe.Pointer(ptr)) = complex64(v)
case 16:
- *(*complex128)(unsafe.Pointer(&s)) = v
+ *(*complex128)(unsafe.Pointer(ptr)) = v
}
- return Value{typ, nil, s, f | flag(typ.Kind())<<flagKindShift}
+ return Value{typ, ptr, f | flagIndir | flag(typ.Kind())<<flagKindShift}
}
func makeString(f flag, v string, t Type) Value {
@@ -2603,7 +2407,7 @@ func cvtDirect(v Value, typ Type) Value {
ptr = c
f &^= flagAddr
}
- return Value{t, ptr, v.scalar, v.flag&flagRO | f} // v.flag&flagRO|f == f?
+ return Value{t, ptr, v.flag&flagRO | f} // v.flag&flagRO|f == f?
}
// convertOp: concrete -> interface
@@ -2615,7 +2419,7 @@ func cvtT2I(v Value, typ Type) Value {
} else {
ifaceE2I(typ.(*rtype), x, unsafe.Pointer(target))
}
- return Value{typ.common(), unsafe.Pointer(target), 0, v.flag&flagRO | flagIndir | flag(Interface)<<flagKindShift}
+ return Value{typ.common(), unsafe.Pointer(target), v.flag&flagRO | flagIndir | flag(Interface)<<flagKindShift}
}
// convertOp: interface -> interface
diff --git a/src/regexp/syntax/doc.go b/src/regexp/syntax/doc.go
index 8e72c90d3..e5e71f14f 100644
--- a/src/regexp/syntax/doc.go
+++ b/src/regexp/syntax/doc.go
@@ -21,8 +21,8 @@ Single characters:
[^xyz] negated character class
\d Perl character class
\D negated Perl character class
- [:alpha:] ASCII character class
- [:^alpha:] negated ASCII character class
+ [[:alpha:]] ASCII character class
+ [[:^alpha:]] negated ASCII character class
\pN Unicode character class (one-letter name)
\p{Greek} Unicode character class
\PN negated Unicode character class (one-letter name)
@@ -46,14 +46,14 @@ Repetitions:
x{n,}? n or more x, prefer fewer
x{n}? exactly n x
-Implementation restriction: The counting forms x{n} etc. (but not the other
-forms x* etc.) have an upper limit of n=1000. Negative or higher explicit
-counts yield the parse error ErrInvalidRepeatSize.
+Implementation restriction: The counting forms x{n,m}, x{n,}, and x{n}
+reject forms that create a minimum or maximum repetition count above 1000.
+Unlimited repetitions are not subject to this restriction.
Grouping:
(re) numbered capturing group (submatch)
(?P<name>re) named & numbered capturing group (submatch)
- (?:re) non-capturing group (submatch)
+ (?:re) non-capturing group
(?flags) set flags within current group; non-capturing
(?flags:re) set flags during re; non-capturing
@@ -69,7 +69,7 @@ Empty strings:
$ at end of text (like \z not \Z) or line (flag m=true)
\A at beginning of text
\b at ASCII word boundary (\w on one side and \W, \A, or \z on the other)
- \B not an ASCII word boundary
+ \B not at ASCII word boundary
\z at end of text
Escape sequences:
@@ -103,29 +103,29 @@ Named character classes as character class elements:
[\p{Name}] named Unicode property inside character class (== \p{Name})
[^\p{Name}] named Unicode property inside negated character class (== \P{Name})
-Perl character classes:
+Perl character classes (all ASCII-only):
\d digits (== [0-9])
\D not digits (== [^0-9])
\s whitespace (== [\t\n\f\r ])
\S not whitespace (== [^\t\n\f\r ])
- \w ASCII word characters (== [0-9A-Za-z_])
- \W not ASCII word characters (== [^0-9A-Za-z_])
+ \w word characters (== [0-9A-Za-z_])
+ \W not word characters (== [^0-9A-Za-z_])
ASCII character classes:
- [:alnum:] alphanumeric (== [0-9A-Za-z])
- [:alpha:] alphabetic (== [A-Za-z])
- [:ascii:] ASCII (== [\x00-\x7F])
- [:blank:] blank (== [\t ])
- [:cntrl:] control (== [\x00-\x1F\x7F])
- [:digit:] digits (== [0-9])
- [:graph:] graphical (== [!-~] == [A-Za-z0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])
- [:lower:] lower case (== [a-z])
- [:print:] printable (== [ -~] == [ [:graph:]])
- [:punct:] punctuation (== [!-/:-@[-`{-~])
- [:space:] whitespace (== [\t\n\v\f\r ])
- [:upper:] upper case (== [A-Z])
- [:word:] word characters (== [0-9A-Za-z_])
- [:xdigit:] hex digit (== [0-9A-Fa-f])
+ [[:alnum:]] alphanumeric (== [0-9A-Za-z])
+ [[:alpha:]] alphabetic (== [A-Za-z])
+ [[:ascii:]] ASCII (== [\x00-\x7F])
+ [[:blank:]] blank (== [\t ])
+ [[:cntrl:]] control (== [\x00-\x1F\x7F])
+ [[:digit:]] digits (== [0-9])
+ [[:graph:]] graphical (== [!-~] == [A-Za-z0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])
+ [[:lower:]] lower case (== [a-z])
+ [[:print:]] printable (== [ -~] == [ [:graph:]])
+ [[:punct:]] punctuation (== [!-/:-@[-`{-~])
+ [[:space:]] whitespace (== [\t\n\v\f\r ])
+ [[:upper:]] upper case (== [A-Z])
+ [[:word:]] word characters (== [0-9A-Za-z_])
+ [[:xdigit:]] hex digit (== [0-9A-Fa-f])
*/
package syntax
diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s
index 1495246a2..b0ed2d8ce 100644
--- a/src/runtime/asm_386.s
+++ b/src/runtime/asm_386.s
@@ -353,7 +353,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0-0
JMP AX
// Note: can't just "JMP NAME(SB)" - bad inlining results.
-TEXT runtime·reflectcall(SB), NOSPLIT, $0-16
+TEXT ·reflectcall(SB), NOSPLIT, $0-16
MOVL argsize+8(FP), CX
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
@@ -385,21 +385,9 @@ TEXT runtime·reflectcall(SB), NOSPLIT, $0-16
MOVL $runtime·badreflectcall(SB), AX
JMP AX
-// Argument map for the callXX frames. Each has one stack map.
-DATA gcargs_reflectcall<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gcargs_reflectcall<>+0x04(SB)/4, $8 // 4 words
-DATA gcargs_reflectcall<>+0x08(SB)/1, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsScalar<<4)+(const_BitsScalar<<6))
-GLOBL gcargs_reflectcall<>(SB),RODATA,$12
-
-// callXX frames have no locals
-DATA gclocals_reflectcall<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gclocals_reflectcall<>+0x04(SB)/4, $0 // 0 locals
-GLOBL gclocals_reflectcall<>(SB),RODATA,$8
-
#define CALLFN(NAME,MAXSIZE) \
-TEXT NAME(SB), WRAPPER, $MAXSIZE-16; \
- FUNCDATA $FUNCDATA_ArgsPointerMaps,gcargs_reflectcall<>(SB); \
- FUNCDATA $FUNCDATA_LocalsPointerMaps,gclocals_reflectcall<>(SB);\
+TEXT NAME(SB), WRAPPER, $MAXSIZE-16; \
+ NO_LOCAL_POINTERS; \
/* copy arguments to stack */ \
MOVL argptr+4(FP), SI; \
MOVL argsize+8(FP), CX; \
@@ -421,33 +409,33 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-16; \
REP;MOVSB; \
RET
-CALLFN(runtime·call16, 16)
-CALLFN(runtime·call32, 32)
-CALLFN(runtime·call64, 64)
-CALLFN(runtime·call128, 128)
-CALLFN(runtime·call256, 256)
-CALLFN(runtime·call512, 512)
-CALLFN(runtime·call1024, 1024)
-CALLFN(runtime·call2048, 2048)
-CALLFN(runtime·call4096, 4096)
-CALLFN(runtime·call8192, 8192)
-CALLFN(runtime·call16384, 16384)
-CALLFN(runtime·call32768, 32768)
-CALLFN(runtime·call65536, 65536)
-CALLFN(runtime·call131072, 131072)
-CALLFN(runtime·call262144, 262144)
-CALLFN(runtime·call524288, 524288)
-CALLFN(runtime·call1048576, 1048576)
-CALLFN(runtime·call2097152, 2097152)
-CALLFN(runtime·call4194304, 4194304)
-CALLFN(runtime·call8388608, 8388608)
-CALLFN(runtime·call16777216, 16777216)
-CALLFN(runtime·call33554432, 33554432)
-CALLFN(runtime·call67108864, 67108864)
-CALLFN(runtime·call134217728, 134217728)
-CALLFN(runtime·call268435456, 268435456)
-CALLFN(runtime·call536870912, 536870912)
-CALLFN(runtime·call1073741824, 1073741824)
+CALLFN(·call16, 16)
+CALLFN(·call32, 32)
+CALLFN(·call64, 64)
+CALLFN(·call128, 128)
+CALLFN(·call256, 256)
+CALLFN(·call512, 512)
+CALLFN(·call1024, 1024)
+CALLFN(·call2048, 2048)
+CALLFN(·call4096, 4096)
+CALLFN(·call8192, 8192)
+CALLFN(·call16384, 16384)
+CALLFN(·call32768, 32768)
+CALLFN(·call65536, 65536)
+CALLFN(·call131072, 131072)
+CALLFN(·call262144, 262144)
+CALLFN(·call524288, 524288)
+CALLFN(·call1048576, 1048576)
+CALLFN(·call2097152, 2097152)
+CALLFN(·call4194304, 4194304)
+CALLFN(·call8388608, 8388608)
+CALLFN(·call16777216, 16777216)
+CALLFN(·call33554432, 33554432)
+CALLFN(·call67108864, 67108864)
+CALLFN(·call134217728, 134217728)
+CALLFN(·call268435456, 268435456)
+CALLFN(·call536870912, 536870912)
+CALLFN(·call1073741824, 1073741824)
// bool cas(int32 *val, int32 old, int32 new)
// Atomically:
@@ -479,6 +467,9 @@ TEXT runtime·atomicloaduintptr(SB), NOSPLIT, $0-8
TEXT runtime·atomicloaduint(SB), NOSPLIT, $0-8
JMP runtime·atomicload(SB)
+TEXT runtime·atomicstoreuintptr(SB), NOSPLIT, $0-8
+ JMP runtime·atomicstore(SB)
+
// bool runtime·cas64(uint64 *val, uint64 old, uint64 new)
// Atomically:
// if(*val == *old){
diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s
index 3f7f60841..2ee331208 100644
--- a/src/runtime/asm_amd64.s
+++ b/src/runtime/asm_amd64.s
@@ -339,11 +339,11 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0
#define DISPATCH(NAME,MAXSIZE) \
CMPQ CX, $MAXSIZE; \
JA 3(PC); \
- MOVQ $NAME(SB), AX; \
+ MOVQ $NAME(SB), AX; \
JMP AX
// Note: can't just "JMP NAME(SB)" - bad inlining results.
-TEXT runtime·reflectcall(SB), NOSPLIT, $0-24
+TEXT ·reflectcall(SB), NOSPLIT, $0-24
MOVLQZX argsize+16(FP), CX
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
@@ -375,21 +375,9 @@ TEXT runtime·reflectcall(SB), NOSPLIT, $0-24
MOVQ $runtime·badreflectcall(SB), AX
JMP AX
-// Argument map for the callXX frames. Each has one stack map.
-DATA gcargs_reflectcall<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gcargs_reflectcall<>+0x04(SB)/4, $6 // 3 words
-DATA gcargs_reflectcall<>+0x08(SB)/1, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsScalar<<4))
-GLOBL gcargs_reflectcall<>(SB),RODATA,$12
-
-// callXX frames have no locals
-DATA gclocals_reflectcall<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gclocals_reflectcall<>+0x04(SB)/4, $0 // 0 locals
-GLOBL gclocals_reflectcall<>(SB),RODATA,$8
-
#define CALLFN(NAME,MAXSIZE) \
TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
- FUNCDATA $FUNCDATA_ArgsPointerMaps,gcargs_reflectcall<>(SB); \
- FUNCDATA $FUNCDATA_LocalsPointerMaps,gclocals_reflectcall<>(SB);\
+ NO_LOCAL_POINTERS; \
/* copy arguments to stack */ \
MOVQ argptr+8(FP), SI; \
MOVLQZX argsize+16(FP), CX; \
@@ -410,33 +398,33 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
REP;MOVSB; \
RET
-CALLFN(runtime·call16, 16)
-CALLFN(runtime·call32, 32)
-CALLFN(runtime·call64, 64)
-CALLFN(runtime·call128, 128)
-CALLFN(runtime·call256, 256)
-CALLFN(runtime·call512, 512)
-CALLFN(runtime·call1024, 1024)
-CALLFN(runtime·call2048, 2048)
-CALLFN(runtime·call4096, 4096)
-CALLFN(runtime·call8192, 8192)
-CALLFN(runtime·call16384, 16384)
-CALLFN(runtime·call32768, 32768)
-CALLFN(runtime·call65536, 65536)
-CALLFN(runtime·call131072, 131072)
-CALLFN(runtime·call262144, 262144)
-CALLFN(runtime·call524288, 524288)
-CALLFN(runtime·call1048576, 1048576)
-CALLFN(runtime·call2097152, 2097152)
-CALLFN(runtime·call4194304, 4194304)
-CALLFN(runtime·call8388608, 8388608)
-CALLFN(runtime·call16777216, 16777216)
-CALLFN(runtime·call33554432, 33554432)
-CALLFN(runtime·call67108864, 67108864)
-CALLFN(runtime·call134217728, 134217728)
-CALLFN(runtime·call268435456, 268435456)
-CALLFN(runtime·call536870912, 536870912)
-CALLFN(runtime·call1073741824, 1073741824)
+CALLFN(·call16, 16)
+CALLFN(·call32, 32)
+CALLFN(·call64, 64)
+CALLFN(·call128, 128)
+CALLFN(·call256, 256)
+CALLFN(·call512, 512)
+CALLFN(·call1024, 1024)
+CALLFN(·call2048, 2048)
+CALLFN(·call4096, 4096)
+CALLFN(·call8192, 8192)
+CALLFN(·call16384, 16384)
+CALLFN(·call32768, 32768)
+CALLFN(·call65536, 65536)
+CALLFN(·call131072, 131072)
+CALLFN(·call262144, 262144)
+CALLFN(·call524288, 524288)
+CALLFN(·call1048576, 1048576)
+CALLFN(·call2097152, 2097152)
+CALLFN(·call4194304, 4194304)
+CALLFN(·call8388608, 8388608)
+CALLFN(·call16777216, 16777216)
+CALLFN(·call33554432, 33554432)
+CALLFN(·call67108864, 67108864)
+CALLFN(·call134217728, 134217728)
+CALLFN(·call268435456, 268435456)
+CALLFN(·call536870912, 536870912)
+CALLFN(·call1073741824, 1073741824)
// bool cas(int32 *val, int32 old, int32 new)
// Atomically:
@@ -491,6 +479,9 @@ TEXT runtime·atomicloaduintptr(SB), NOSPLIT, $0-16
TEXT runtime·atomicloaduint(SB), NOSPLIT, $0-16
JMP runtime·atomicload64(SB)
+TEXT runtime·atomicstoreuintptr(SB), NOSPLIT, $0-16
+ JMP runtime·atomicstore64(SB)
+
// bool casp(void **val, void *old, void *new)
// Atomically:
// if(*val == old){
diff --git a/src/runtime/asm_amd64p32.s b/src/runtime/asm_amd64p32.s
index 13a164256..e27f67e1e 100644
--- a/src/runtime/asm_amd64p32.s
+++ b/src/runtime/asm_amd64p32.s
@@ -310,11 +310,11 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0
#define DISPATCH(NAME,MAXSIZE) \
CMPL CX, $MAXSIZE; \
JA 3(PC); \
- MOVL $NAME(SB), AX; \
+ MOVL $NAME(SB), AX; \
JMP AX
// Note: can't just "JMP NAME(SB)" - bad inlining results.
-TEXT runtime·reflectcall(SB), NOSPLIT, $0-16
+TEXT ·reflectcall(SB), NOSPLIT, $0-16
MOVLQZX argsize+8(FP), CX
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
@@ -346,22 +346,9 @@ TEXT runtime·reflectcall(SB), NOSPLIT, $0-16
MOVL $runtime·badreflectcall(SB), AX
JMP AX
-// Argument map for the callXX frames. Each has one stack map.
-DATA gcargs_reflectcall<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gcargs_reflectcall<>+0x04(SB)/4, $10 // 5 words
-DATA gcargs_reflectcall<>+0x08(SB)/1, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsScalar<<4)+(const_BitsScalar<<6))
-DATA gcargs_reflectcall<>+0x09(SB)/1, $(const_BitsPointer)
-GLOBL gcargs_reflectcall<>(SB),RODATA,$12
-
-// callXX frames have no locals
-DATA gclocals_reflectcall<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gclocals_reflectcall<>+0x04(SB)/4, $0 // 0 locals
-GLOBL gclocals_reflectcall<>(SB),RODATA,$8
-
#define CALLFN(NAME,MAXSIZE) \
TEXT NAME(SB), WRAPPER, $MAXSIZE-16; \
- FUNCDATA $FUNCDATA_ArgsPointerMaps,gcargs_reflectcall<>(SB); \
- FUNCDATA $FUNCDATA_LocalsPointerMaps,gclocals_reflectcall<>(SB);\
+ NO_LOCAL_POINTERS; \
/* copy arguments to stack */ \
MOVL argptr+4(FP), SI; \
MOVL argsize+8(FP), CX; \
@@ -369,8 +356,8 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-16; \
REP;MOVSB; \
/* call function */ \
MOVL f+0(FP), DX; \
- MOVL (DX), AX; \
- CALL AX; \
+ MOVL (DX), AX; \
+ CALL AX; \
/* copy return values back */ \
MOVL argptr+4(FP), DI; \
MOVL argsize+8(FP), CX; \
@@ -382,33 +369,33 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-16; \
REP;MOVSB; \
RET
-CALLFN(runtime·call16, 16)
-CALLFN(runtime·call32, 32)
-CALLFN(runtime·call64, 64)
-CALLFN(runtime·call128, 128)
-CALLFN(runtime·call256, 256)
-CALLFN(runtime·call512, 512)
-CALLFN(runtime·call1024, 1024)
-CALLFN(runtime·call2048, 2048)
-CALLFN(runtime·call4096, 4096)
-CALLFN(runtime·call8192, 8192)
-CALLFN(runtime·call16384, 16384)
-CALLFN(runtime·call32768, 32768)
-CALLFN(runtime·call65536, 65536)
-CALLFN(runtime·call131072, 131072)
-CALLFN(runtime·call262144, 262144)
-CALLFN(runtime·call524288, 524288)
-CALLFN(runtime·call1048576, 1048576)
-CALLFN(runtime·call2097152, 2097152)
-CALLFN(runtime·call4194304, 4194304)
-CALLFN(runtime·call8388608, 8388608)
-CALLFN(runtime·call16777216, 16777216)
-CALLFN(runtime·call33554432, 33554432)
-CALLFN(runtime·call67108864, 67108864)
-CALLFN(runtime·call134217728, 134217728)
-CALLFN(runtime·call268435456, 268435456)
-CALLFN(runtime·call536870912, 536870912)
-CALLFN(runtime·call1073741824, 1073741824)
+CALLFN(·call16, 16)
+CALLFN(·call32, 32)
+CALLFN(·call64, 64)
+CALLFN(·call128, 128)
+CALLFN(·call256, 256)
+CALLFN(·call512, 512)
+CALLFN(·call1024, 1024)
+CALLFN(·call2048, 2048)
+CALLFN(·call4096, 4096)
+CALLFN(·call8192, 8192)
+CALLFN(·call16384, 16384)
+CALLFN(·call32768, 32768)
+CALLFN(·call65536, 65536)
+CALLFN(·call131072, 131072)
+CALLFN(·call262144, 262144)
+CALLFN(·call524288, 524288)
+CALLFN(·call1048576, 1048576)
+CALLFN(·call2097152, 2097152)
+CALLFN(·call4194304, 4194304)
+CALLFN(·call8388608, 8388608)
+CALLFN(·call16777216, 16777216)
+CALLFN(·call33554432, 33554432)
+CALLFN(·call67108864, 67108864)
+CALLFN(·call134217728, 134217728)
+CALLFN(·call268435456, 268435456)
+CALLFN(·call536870912, 536870912)
+CALLFN(·call1073741824, 1073741824)
// bool cas(int32 *val, int32 old, int32 new)
// Atomically:
@@ -440,6 +427,9 @@ TEXT runtime·atomicloaduintptr(SB), NOSPLIT, $0-12
TEXT runtime·atomicloaduint(SB), NOSPLIT, $0-12
JMP runtime·atomicload(SB)
+TEXT runtime·atomicstoreuintptr(SB), NOSPLIT, $0-12
+ JMP runtime·atomicstore(SB)
+
// bool runtime·cas64(uint64 *val, uint64 old, uint64 new)
// Atomically:
// if(*val == *old){
diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s
index 36fb022f9..b21441488 100644
--- a/src/runtime/asm_arm.s
+++ b/src/runtime/asm_arm.s
@@ -343,7 +343,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$-4-0
MOVW $NAME(SB), R1; \
B (R1)
-TEXT runtime·reflectcall(SB),NOSPLIT,$-4-16
+TEXT ·reflectcall(SB),NOSPLIT,$-4-16
MOVW argsize+8(FP), R0
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
@@ -375,21 +375,9 @@ TEXT runtime·reflectcall(SB),NOSPLIT,$-4-16
MOVW $runtime·badreflectcall(SB), R1
B (R1)
-// Argument map for the callXX frames. Each has one stack map.
-DATA gcargs_reflectcall<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gcargs_reflectcall<>+0x04(SB)/4, $8 // 4 words
-DATA gcargs_reflectcall<>+0x08(SB)/1, $(const_BitsPointer+(const_BitsPointer<<2)+(const_BitsScalar<<4)+(const_BitsScalar<<6))
-GLOBL gcargs_reflectcall<>(SB),RODATA,$12
-
-// callXX frames have no locals
-DATA gclocals_reflectcall<>+0x00(SB)/4, $1 // 1 stackmap
-DATA gclocals_reflectcall<>+0x04(SB)/4, $0 // 0 locals
-GLOBL gclocals_reflectcall<>(SB),RODATA,$8
-
#define CALLFN(NAME,MAXSIZE) \
TEXT NAME(SB), WRAPPER, $MAXSIZE-16; \
- FUNCDATA $FUNCDATA_ArgsPointerMaps,gcargs_reflectcall<>(SB); \
- FUNCDATA $FUNCDATA_LocalsPointerMaps,gclocals_reflectcall<>(SB);\
+ NO_LOCAL_POINTERS; \
/* copy arguments to stack */ \
MOVW argptr+4(FP), R0; \
MOVW argsize+8(FP), R2; \
@@ -420,33 +408,33 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-16; \
SUB $1, R2, R2; \
B -5(PC) \
-CALLFN(runtime·call16, 16)
-CALLFN(runtime·call32, 32)
-CALLFN(runtime·call64, 64)
-CALLFN(runtime·call128, 128)
-CALLFN(runtime·call256, 256)
-CALLFN(runtime·call512, 512)
-CALLFN(runtime·call1024, 1024)
-CALLFN(runtime·call2048, 2048)
-CALLFN(runtime·call4096, 4096)
-CALLFN(runtime·call8192, 8192)
-CALLFN(runtime·call16384, 16384)
-CALLFN(runtime·call32768, 32768)
-CALLFN(runtime·call65536, 65536)
-CALLFN(runtime·call131072, 131072)
-CALLFN(runtime·call262144, 262144)
-CALLFN(runtime·call524288, 524288)
-CALLFN(runtime·call1048576, 1048576)
-CALLFN(runtime·call2097152, 2097152)
-CALLFN(runtime·call4194304, 4194304)
-CALLFN(runtime·call8388608, 8388608)
-CALLFN(runtime·call16777216, 16777216)
-CALLFN(runtime·call33554432, 33554432)
-CALLFN(runtime·call67108864, 67108864)
-CALLFN(runtime·call134217728, 134217728)
-CALLFN(runtime·call268435456, 268435456)
-CALLFN(runtime·call536870912, 536870912)
-CALLFN(runtime·call1073741824, 1073741824)
+CALLFN(·call16, 16)
+CALLFN(·call32, 32)
+CALLFN(·call64, 64)
+CALLFN(·call128, 128)
+CALLFN(·call256, 256)
+CALLFN(·call512, 512)
+CALLFN(·call1024, 1024)
+CALLFN(·call2048, 2048)
+CALLFN(·call4096, 4096)
+CALLFN(·call8192, 8192)
+CALLFN(·call16384, 16384)
+CALLFN(·call32768, 32768)
+CALLFN(·call65536, 65536)
+CALLFN(·call131072, 131072)
+CALLFN(·call262144, 262144)
+CALLFN(·call524288, 524288)
+CALLFN(·call1048576, 1048576)
+CALLFN(·call2097152, 2097152)
+CALLFN(·call4194304, 4194304)
+CALLFN(·call8388608, 8388608)
+CALLFN(·call16777216, 16777216)
+CALLFN(·call33554432, 33554432)
+CALLFN(·call67108864, 67108864)
+CALLFN(·call134217728, 134217728)
+CALLFN(·call268435456, 268435456)
+CALLFN(·call536870912, 536870912)
+CALLFN(·call1073741824, 1073741824)
// void jmpdefer(fn, sp);
// called from deferreturn.
@@ -724,6 +712,9 @@ TEXT runtime·atomicloaduintptr(SB),NOSPLIT,$0-8
TEXT runtime·atomicloaduint(SB),NOSPLIT,$0-8
B runtime·atomicload(SB)
+TEXT runtime·atomicstoreuintptr(SB),NOSPLIT,$0-8
+ B runtime·atomicstore(SB)
+
// AES hashing not implemented for ARM
TEXT runtime·aeshash(SB),NOSPLIT,$-4-0
MOVW $0, R0
diff --git a/src/runtime/cgocallback.go b/src/runtime/cgocallback.go
index 1e1b57607..2c8914320 100644
--- a/src/runtime/cgocallback.go
+++ b/src/runtime/cgocallback.go
@@ -21,6 +21,9 @@ import "unsafe"
// Either we need to add types or we need to stop using it.
func _cgo_allocate_internal(len uintptr) unsafe.Pointer {
+ if len == 0 {
+ len = 1
+ }
ret := unsafe.Pointer(&make([]unsafe.Pointer, (len+ptrSize-1)/ptrSize)[0])
c := new(cgomal)
c.alloc = ret
diff --git a/src/runtime/chan.go b/src/runtime/chan.go
index 10503f4e1..004970182 100644
--- a/src/runtime/chan.go
+++ b/src/runtime/chan.go
@@ -174,6 +174,10 @@ func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uin
goparkunlock(&c.lock, "chan send")
// someone woke us up.
+ if mysg != gp.waiting {
+ gothrow("G waiting list is corrupted!")
+ }
+ gp.waiting = nil
if gp.param == nil {
if c.closed == 0 {
gothrow("chansend: spurious wakeup")
@@ -184,10 +188,6 @@ func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uin
if mysg.releasetime > 0 {
blockevent(int64(mysg.releasetime)-t0, 2)
}
- if mysg != gp.waiting {
- gothrow("G waiting list is corrupted!")
- }
- gp.waiting = nil
releaseSudog(mysg)
return true
}
@@ -410,6 +410,9 @@ func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, r
goparkunlock(&c.lock, "chan receive")
// someone woke us up
+ if mysg != gp.waiting {
+ gothrow("G waiting list is corrupted!")
+ }
gp.waiting = nil
if mysg.releasetime > 0 {
blockevent(mysg.releasetime-t0, 2)
diff --git a/src/runtime/defs_windows.go b/src/runtime/defs_windows.go
index cb0f54d8a..7ce679741 100644
--- a/src/runtime/defs_windows.go
+++ b/src/runtime/defs_windows.go
@@ -49,6 +49,7 @@ const (
CONTEXT_FULL = C.CONTEXT_FULL
EXCEPTION_ACCESS_VIOLATION = C.STATUS_ACCESS_VIOLATION
+ EXCEPTION_BREAKPOINT = C.STATUS_BREAKPOINT
EXCEPTION_FLT_DENORMAL_OPERAND = C.STATUS_FLOAT_DENORMAL_OPERAND
EXCEPTION_FLT_DIVIDE_BY_ZERO = C.STATUS_FLOAT_DIVIDE_BY_ZERO
EXCEPTION_FLT_INEXACT_RESULT = C.STATUS_FLOAT_INEXACT_RESULT
@@ -59,6 +60,9 @@ const (
INFINITE = C.INFINITE
WAIT_TIMEOUT = C.WAIT_TIMEOUT
+
+ EXCEPTION_CONTINUE_EXECUTION = C.EXCEPTION_CONTINUE_EXECUTION
+ EXCEPTION_CONTINUE_SEARCH = C.EXCEPTION_CONTINUE_SEARCH
)
type SystemInfo C.SYSTEM_INFO
diff --git a/src/runtime/defs_windows_386.h b/src/runtime/defs_windows_386.h
index 295e422c6..2317c04f6 100644
--- a/src/runtime/defs_windows_386.h
+++ b/src/runtime/defs_windows_386.h
@@ -22,6 +22,7 @@ enum {
CONTEXT_FULL = 0x10007,
EXCEPTION_ACCESS_VIOLATION = 0xc0000005,
+ EXCEPTION_BREAKPOINT = 0x80000003,
EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d,
EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e,
EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f,
@@ -32,6 +33,9 @@ enum {
INFINITE = 0xffffffff,
WAIT_TIMEOUT = 0x102,
+
+ EXCEPTION_CONTINUE_EXECUTION = -0x1,
+ EXCEPTION_CONTINUE_SEARCH = 0x0,
};
typedef struct SystemInfo SystemInfo;
diff --git a/src/runtime/defs_windows_amd64.h b/src/runtime/defs_windows_amd64.h
index 2516c8412..7f37a7a8c 100644
--- a/src/runtime/defs_windows_amd64.h
+++ b/src/runtime/defs_windows_amd64.h
@@ -22,6 +22,7 @@ enum {
CONTEXT_FULL = 0x10000b,
EXCEPTION_ACCESS_VIOLATION = 0xc0000005,
+ EXCEPTION_BREAKPOINT = 0x80000003,
EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d,
EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e,
EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f,
@@ -32,6 +33,9 @@ enum {
INFINITE = 0xffffffff,
WAIT_TIMEOUT = 0x102,
+
+ EXCEPTION_CONTINUE_EXECUTION = -0x1,
+ EXCEPTION_CONTINUE_SEARCH = 0x0,
};
typedef struct SystemInfo SystemInfo;
diff --git a/src/runtime/heapdump.c b/src/runtime/heapdump.c
index 54b9666b5..71da419f1 100644
--- a/src/runtime/heapdump.c
+++ b/src/runtime/heapdump.c
@@ -7,7 +7,7 @@
// finalizers, etc.) to a file.
// The format of the dumped file is described at
-// http://code.google.com/p/go-wiki/wiki/heapdump13
+// http://code.google.com/p/go-wiki/wiki/heapdump14
#include "runtime.h"
#include "arch_GOARCH.h"
@@ -27,10 +27,8 @@ extern byte runtime·ebss[];
enum {
FieldKindEol = 0,
FieldKindPtr = 1,
- FieldKindString = 2,
- FieldKindSlice = 3,
- FieldKindIface = 4,
- FieldKindEface = 5,
+ FieldKindIface = 2,
+ FieldKindEface = 3,
TagEOF = 0,
TagObject = 1,
@@ -200,7 +198,6 @@ dumptype(Type *t)
write(t->x->name->str, t->x->name->len);
}
dumpbool((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0);
- dumpfields((BitVector){0, nil});
}
// dump an object
@@ -210,9 +207,8 @@ dumpobj(byte *obj, uintptr size, BitVector bv)
dumpbvtypes(&bv, obj);
dumpint(TagObject);
dumpint((uintptr)obj);
- dumpint(0); // Type*
- dumpint(0); // kind
dumpmemrange(obj, size);
+ dumpfields(bv);
}
static void
@@ -255,6 +251,7 @@ dumpbv(BitVector *bv, uintptr offset)
for(i = 0; i < bv->n; i += BitsPerPointer) {
switch(bv->bytedata[i/8] >> i%8 & 3) {
case BitsDead:
+ return;
case BitsScalar:
break;
case BitsPointer:
@@ -489,16 +486,18 @@ dumproots(void)
byte *p;
// data segment
+ dumpbvtypes(&runtime·gcdatamask, runtime·data);
dumpint(TagData);
dumpint((uintptr)runtime·data);
dumpmemrange(runtime·data, runtime·edata - runtime·data);
dumpfields(runtime·gcdatamask);
// bss segment
+ dumpbvtypes(&runtime·gcbssmask, runtime·bss);
dumpint(TagBss);
dumpint((uintptr)runtime·bss);
dumpmemrange(runtime·bss, runtime·ebss - runtime·bss);
- dumpfields(runtime·gcdatamask);
+ dumpfields(runtime·gcbssmask);
// MSpan.types
allspans = runtime·mheap.allspans;
@@ -578,10 +577,29 @@ itab_callback(Itab *tab)
{
Type *t;
- dumpint(TagItab);
- dumpint((uintptr)tab);
t = tab->type;
- dumpbool((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0);
+ // Dump a map from itab* to the type of its data field.
+ // We want this map so we can deduce types of interface referents.
+ if((t->kind & KindDirectIface) == 0) {
+ // indirect - data slot is a pointer to t.
+ dumptype(t->ptrto);
+ dumpint(TagItab);
+ dumpint((uintptr)tab);
+ dumpint((uintptr)t->ptrto);
+ } else if((t->kind & KindNoPointers) == 0) {
+ // t is pointer-like - data slot is a t.
+ dumptype(t);
+ dumpint(TagItab);
+ dumpint((uintptr)tab);
+ dumpint((uintptr)t);
+ } else {
+ // Data slot is a scalar. Dump type just for fun.
+ // With pointer-only interfaces, this shouldn't happen.
+ dumptype(t);
+ dumpint(TagItab);
+ dumpint((uintptr)tab);
+ dumpint((uintptr)t);
+ }
}
static void
@@ -726,7 +744,7 @@ mdump(void)
}
runtime·memclr((byte*)&typecache[0], sizeof(typecache));
- hdr = (byte*)"go1.3 heap dump\n";
+ hdr = (byte*)"go1.4 heap dump\n";
write(hdr, runtime·findnull(hdr));
dumpparams();
dumpitabs();
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index 99d14e314..9b4264f2b 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -270,7 +270,7 @@ func mallocgc(size uintptr, typ *_type, flags int) unsafe.Pointer {
}
ptrmask = (*uint8)(add(unsafe.Pointer(ptrmask), 1)) // skip the unroll flag byte
} else {
- ptrmask = (*uint8)(unsafe.Pointer(&typ.gc[0])) // embed mask
+ ptrmask = (*uint8)(unsafe.Pointer(typ.gc[0])) // pointer to unrolled mask
}
if size == 2*ptrSize {
*xbits = *ptrmask | bitBoundary
diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c
index dabd38a60..8620f47af 100644
--- a/src/runtime/mgc0.c
+++ b/src/runtime/mgc0.c
@@ -383,25 +383,19 @@ greyobject(byte *obj, Markbits *mbits, Workbuf *wbuf)
static Workbuf*
scanobject(byte *b, uintptr n, byte *ptrmask, Workbuf *wbuf)
{
- byte *obj, *arena_start, *arena_used, *ptrbitp, bits, cshift, cached;
- uintptr i;
- intptr ncached;
+ byte *obj, *arena_start, *arena_used, *ptrbitp;
+ uintptr i, j;
+ int32 bits;
Markbits mbits;
arena_start = (byte*)runtime·mheap.arena_start;
arena_used = runtime·mheap.arena_used;
ptrbitp = nil;
- cached = 0;
- ncached = 0;
// Find bits of the beginning of the object.
if(ptrmask == nil) {
b = objectstart(b, &mbits);
ptrbitp = mbits.bitp; //arena_start - off/wordsPerBitmapByte - 1;
- cshift = mbits.shift; //(off % wordsPerBitmapByte) * gcBits;
- cached = *ptrbitp >> cshift;
- cached &= ~bitBoundary;
- ncached = (8 - cshift)/gcBits;
}
for(i = 0; i < n; i += PtrSize) {
// Find bits for this word.
@@ -414,26 +408,28 @@ scanobject(byte *b, uintptr n, byte *ptrmask, Workbuf *wbuf)
runtime·mheap.spans[(b-arena_start)>>PageShift] != runtime·mheap.spans[(b+i-arena_start)>>PageShift])
break;
// Consult GC bitmap.
- if(ncached <= 0) {
- // Refill cache.
- cached = *--ptrbitp;
- ncached = 2;
- }
- bits = cached;
- cached >>= gcBits;
- ncached--;
-
- if((bits&bitBoundary) != 0)
+ bits = *ptrbitp;
+ if(wordsPerBitmapByte != 2)
+ runtime·throw("alg doesn't work for wordsPerBitmapByte != 2");
+ j = ((uintptr)b+i)/PtrSize & 1;
+ bits >>= gcBits*j;
+ if(i == 0)
+ bits &= ~bitBoundary;
+ ptrbitp -= j;
+
+ if((bits&bitBoundary) != 0 && i != 0)
break; // reached beginning of the next object
bits = (bits>>2)&BitsMask;
if(bits == BitsDead)
break; // reached no-scan part of the object
}
- if(bits == BitsScalar || bits == BitsDead)
+ if(bits <= BitsScalar) // Bits Scalar || BitsDead
continue;
- if(bits != BitsPointer)
+ if(bits != BitsPointer) {
+ runtime·printf("gc bits=%x\n", bits);
runtime·throw("unexpected garbage collection bits");
+ }
obj = *(byte**)(b+i);
// At this point we have extracted the next potential pointer.
@@ -1608,6 +1604,8 @@ gc(struct gc_args *args)
if(runtime·work.nproc > 1)
runtime·notesleep(&runtime·work.alldone);
+ runtime·shrinkfinish();
+
cachestats();
// next_gc calculation is tricky with concurrent sweep since we don't know size of live heap
// estimate what was live heap size after previous GC (for tracing only)
diff --git a/src/runtime/mgc0.go b/src/runtime/mgc0.go
index 0e17599c2..3a7204b54 100644
--- a/src/runtime/mgc0.go
+++ b/src/runtime/mgc0.go
@@ -110,6 +110,27 @@ func writebarrieriface(dst *[2]uintptr, src [2]uintptr) {
}
//go:nosplit
+func writebarrierfat2(dst *[2]uintptr, _ *byte, src [2]uintptr) {
+ dst[0] = src[0]
+ dst[1] = src[1]
+}
+
+//go:nosplit
+func writebarrierfat3(dst *[3]uintptr, _ *byte, src [3]uintptr) {
+ dst[0] = src[0]
+ dst[1] = src[1]
+ dst[2] = src[2]
+}
+
+//go:nosplit
+func writebarrierfat4(dst *[4]uintptr, _ *byte, src [4]uintptr) {
+ dst[0] = src[0]
+ dst[1] = src[1]
+ dst[2] = src[2]
+ dst[3] = src[3]
+}
+
+//go:nosplit
func writebarrierfat(typ *_type, dst, src unsafe.Pointer) {
memmove(dst, src, typ.size)
}
diff --git a/src/runtime/mgc0.h b/src/runtime/mgc0.h
index 10f24d009..64f818914 100644
--- a/src/runtime/mgc0.h
+++ b/src/runtime/mgc0.h
@@ -42,6 +42,8 @@ enum {
BitsMask = (1<<BitsPerPointer)-1,
PointersPerByte = 8/BitsPerPointer,
+ // If you change these, also change scanblock.
+ // scanblock does "if(bits == BitsScalar || bits == BitsDead)" as "if(bits <= BitsScalar)".
BitsDead = 0,
BitsScalar = 1,
BitsPointer = 2,
diff --git a/src/runtime/os_windows.c b/src/runtime/os_windows.c
index 77f99062c..b8b8eda5f 100644
--- a/src/runtime/os_windows.c
+++ b/src/runtime/os_windows.c
@@ -34,6 +34,7 @@
#pragma dynimport runtime·SetEvent SetEvent "kernel32.dll"
#pragma dynimport runtime·SetProcessPriorityBoost SetProcessPriorityBoost "kernel32.dll"
#pragma dynimport runtime·SetThreadPriority SetThreadPriority "kernel32.dll"
+#pragma dynimport runtime·SetUnhandledExceptionFilter SetUnhandledExceptionFilter "kernel32.dll"
#pragma dynimport runtime·SetWaitableTimer SetWaitableTimer "kernel32.dll"
#pragma dynimport runtime·Sleep Sleep "kernel32.dll"
#pragma dynimport runtime·SuspendThread SuspendThread "kernel32.dll"
@@ -65,6 +66,7 @@ extern void *runtime·SetConsoleCtrlHandler;
extern void *runtime·SetEvent;
extern void *runtime·SetProcessPriorityBoost;
extern void *runtime·SetThreadPriority;
+extern void *runtime·SetUnhandledExceptionFilter;
extern void *runtime·SetWaitableTimer;
extern void *runtime·Sleep;
extern void *runtime·SuspendThread;
@@ -77,7 +79,9 @@ void *runtime·GetQueuedCompletionStatusEx;
extern uintptr runtime·externalthreadhandlerp;
void runtime·externalthreadhandler(void);
-void runtime·sigtramp(void);
+void runtime·exceptiontramp(void);
+void runtime·firstcontinuetramp(void);
+void runtime·lastcontinuetramp(void);
#pragma textflag NOSPLIT
uintptr
@@ -106,12 +110,30 @@ void
runtime·osinit(void)
{
void *kernel32;
+ void *addVectoredContinueHandler;
+
+ kernel32 = runtime·stdcall1(runtime·LoadLibraryA, (uintptr)"kernel32.dll");
runtime·externalthreadhandlerp = (uintptr)runtime·externalthreadhandler;
- runtime·stdcall2(runtime·AddVectoredExceptionHandler, 1, (uintptr)runtime·sigtramp);
+ runtime·stdcall2(runtime·AddVectoredExceptionHandler, 1, (uintptr)runtime·exceptiontramp);
+ addVectoredContinueHandler = nil;
+ if(kernel32 != nil)
+ addVectoredContinueHandler = runtime·stdcall2(runtime·GetProcAddress, (uintptr)kernel32, (uintptr)"AddVectoredContinueHandler");
+ if(addVectoredContinueHandler == nil || sizeof(void*) == 4) {
+ // use SetUnhandledExceptionFilter for windows-386 or
+ // if VectoredContinueHandler is unavailable.
+ // note: SetUnhandledExceptionFilter handler won't be called, if debugging.
+ runtime·stdcall1(runtime·SetUnhandledExceptionFilter, (uintptr)runtime·lastcontinuetramp);
+ } else {
+ runtime·stdcall2(addVectoredContinueHandler, 1, (uintptr)runtime·firstcontinuetramp);
+ runtime·stdcall2(addVectoredContinueHandler, 0, (uintptr)runtime·lastcontinuetramp);
+ }
+
runtime·stdcall2(runtime·SetConsoleCtrlHandler, (uintptr)runtime·ctrlhandler, 1);
+
runtime·stdcall1(runtime·timeBeginPeriod, 1);
+
runtime·ncpu = getproccount();
// Windows dynamic priority boosting assumes that a process has different types
@@ -120,7 +142,6 @@ runtime·osinit(void)
// In such context dynamic priority boosting does nothing but harm, so we turn it off.
runtime·stdcall2(runtime·SetProcessPriorityBoost, -1, 1);
- kernel32 = runtime·stdcall1(runtime·LoadLibraryA, (uintptr)"kernel32.dll");
if(kernel32 != nil) {
runtime·GetQueuedCompletionStatusEx = runtime·stdcall2(runtime·GetProcAddress, (uintptr)kernel32, (uintptr)"GetQueuedCompletionStatusEx");
}
@@ -268,19 +289,19 @@ runtime·mpreinit(M *mp)
void
runtime·minit(void)
{
- void *thandle;
+ uintptr thandle;
// -1 = current process, -2 = current thread
runtime·stdcall7(runtime·DuplicateHandle, -1, -2, -1, (uintptr)&thandle, 0, 0, DUPLICATE_SAME_ACCESS);
- runtime·atomicstorep(&g->m->thread, thandle);
+ runtime·atomicstoreuintptr(&g->m->thread, thandle);
}
// Called from dropm to undo the effect of an minit.
void
runtime·unminit(void)
{
- runtime·stdcall1(runtime·CloseHandle, (uintptr)g->m->thread);
- g->m->thread = nil;
+ runtime·stdcall1(runtime·CloseHandle, g->m->thread);
+ g->m->thread = 0;
}
// Described in http://www.dcl.hpi.uni-potsdam.de/research/WRK/2007/08/getting-os-information-the-kuser_shared_data-structure/
@@ -467,6 +488,7 @@ runtime·issigpanic(uint32 code)
case EXCEPTION_FLT_INEXACT_RESULT:
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_UNDERFLOW:
+ case EXCEPTION_BREAKPOINT:
return 1;
}
return 0;
@@ -475,10 +497,14 @@ runtime·issigpanic(uint32 code)
void
runtime·initsig(void)
{
- // following line keeps sigtramp alive at link stage
+ // following line keeps these functions alive at link stage
// if there's a better way please write it here
- void *p = runtime·sigtramp;
- USED(p);
+ void *e = runtime·exceptiontramp;
+ void *f = runtime·firstcontinuetramp;
+ void *l = runtime·lastcontinuetramp;
+ USED(e);
+ USED(f);
+ USED(l);
}
uint32
@@ -532,7 +558,7 @@ void
runtime·profileloop1(void)
{
M *mp, *allm;
- void *thread;
+ uintptr thread;
runtime·stdcall2(runtime·SetThreadPriority, -2, THREAD_PRIORITY_HIGHEST);
@@ -540,11 +566,11 @@ runtime·profileloop1(void)
runtime·stdcall2(runtime·WaitForSingleObject, (uintptr)profiletimer, -1);
allm = runtime·atomicloadp(&runtime·allm);
for(mp = allm; mp != nil; mp = mp->alllink) {
- thread = runtime·atomicloadp(&mp->thread);
+ thread = runtime·atomicloaduintptr(&mp->thread);
// Do not profile threads blocked on Notes,
// this includes idle worker threads,
// idle timer thread, idle heap scavenger, etc.
- if(thread == nil || mp->profilehz == 0 || mp->blocked)
+ if(thread == 0 || mp->profilehz == 0 || mp->blocked)
continue;
runtime·stdcall1(runtime·SuspendThread, (uintptr)thread);
if(mp->profilehz != 0 && !mp->blocked)
diff --git a/src/runtime/os_windows_386.c b/src/runtime/os_windows_386.c
index e2ae8db27..213582799 100644
--- a/src/runtime/os_windows_386.c
+++ b/src/runtime/os_windows_386.c
@@ -24,45 +24,63 @@ runtime·dumpregs(Context *r)
runtime·printf("gs %x\n", r->SegGs);
}
-// Called by sigtramp from Windows VEH handler.
-// Return value signals whether the exception has been handled (-1)
-// or should be made available to other handlers in the chain (0).
-uint32
-runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
+bool
+runtime·isgoexception(ExceptionRecord *info, Context *r)
{
- bool crash;
- uintptr *sp;
extern byte runtime·text[], runtime·etext[];
// Only handle exception if executing instructions in Go binary
// (not Windows library code).
if(r->Eip < (uint32)runtime·text || (uint32)runtime·etext < r->Eip)
- return 0;
-
- if(gp != nil && runtime·issigpanic(info->ExceptionCode)) {
- // Make it look like a call to the signal func.
- // Have to pass arguments out of band since
- // augmenting the stack frame would break
- // the unwinding code.
- gp->sig = info->ExceptionCode;
- gp->sigcode0 = info->ExceptionInformation[0];
- gp->sigcode1 = info->ExceptionInformation[1];
- gp->sigpc = r->Eip;
-
- // Only push runtime·sigpanic if r->eip != 0.
- // If r->eip == 0, probably panicked because of a
- // call to a nil func. Not pushing that onto sp will
- // make the trace look like a call to runtime·sigpanic instead.
- // (Otherwise the trace will end at runtime·sigpanic and we
- // won't get to see who faulted.)
- if(r->Eip != 0) {
- sp = (uintptr*)r->Esp;
- *--sp = r->Eip;
- r->Esp = (uintptr)sp;
- }
- r->Eip = (uintptr)runtime·sigpanic;
- return -1;
+ return false;
+
+ if(!runtime·issigpanic(info->ExceptionCode))
+ return false;
+
+ return true;
+}
+
+// Called by sigtramp from Windows VEH handler.
+// Return value signals whether the exception has been handled (EXCEPTION_CONTINUE_EXECUTION)
+// or should be made available to other handlers in the chain (EXCEPTION_CONTINUE_SEARCH).
+uint32
+runtime·exceptionhandler(ExceptionRecord *info, Context *r, G *gp)
+{
+ uintptr *sp;
+
+ if(!runtime·isgoexception(info, r))
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ // Make it look like a call to the signal func.
+ // Have to pass arguments out of band since
+ // augmenting the stack frame would break
+ // the unwinding code.
+ gp->sig = info->ExceptionCode;
+ gp->sigcode0 = info->ExceptionInformation[0];
+ gp->sigcode1 = info->ExceptionInformation[1];
+ gp->sigpc = r->Eip;
+
+ // Only push runtime·sigpanic if r->eip != 0.
+ // If r->eip == 0, probably panicked because of a
+ // call to a nil func. Not pushing that onto sp will
+ // make the trace look like a call to runtime·sigpanic instead.
+ // (Otherwise the trace will end at runtime·sigpanic and we
+ // won't get to see who faulted.)
+ if(r->Eip != 0) {
+ sp = (uintptr*)r->Esp;
+ *--sp = r->Eip;
+ r->Esp = (uintptr)sp;
}
+ r->Eip = (uintptr)runtime·sigpanic;
+ return EXCEPTION_CONTINUE_EXECUTION;
+}
+
+// lastcontinuehandler is reached, because runtime cannot handle
+// current exception. lastcontinuehandler will print crash info and exit.
+uint32
+runtime·lastcontinuehandler(ExceptionRecord *info, Context *r, G *gp)
+{
+ bool crash;
if(runtime·panicking) // traceback already printed
runtime·exit(2);
@@ -88,7 +106,7 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
runtime·crash();
runtime·exit(2);
- return -1; // not reached
+ return 0; // not reached
}
void
diff --git a/src/runtime/os_windows_amd64.c b/src/runtime/os_windows_amd64.c
index 261880d45..b96cf70d1 100644
--- a/src/runtime/os_windows_amd64.c
+++ b/src/runtime/os_windows_amd64.c
@@ -32,45 +32,76 @@ runtime·dumpregs(Context *r)
runtime·printf("gs %X\n", (uint64)r->SegGs);
}
-// Called by sigtramp from Windows VEH handler.
-// Return value signals whether the exception has been handled (-1)
-// or should be made available to other handlers in the chain (0).
-uint32
-runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
+bool
+runtime·isgoexception(ExceptionRecord *info, Context *r)
{
- bool crash;
- uintptr *sp;
extern byte runtime·text[], runtime·etext[];
// Only handle exception if executing instructions in Go binary
// (not Windows library code).
if(r->Rip < (uint64)runtime·text || (uint64)runtime·etext < r->Rip)
- return 0;
-
- if(gp != nil && runtime·issigpanic(info->ExceptionCode)) {
- // Make it look like a call to the signal func.
- // Have to pass arguments out of band since
- // augmenting the stack frame would break
- // the unwinding code.
- gp->sig = info->ExceptionCode;
- gp->sigcode0 = info->ExceptionInformation[0];
- gp->sigcode1 = info->ExceptionInformation[1];
- gp->sigpc = r->Rip;
-
- // Only push runtime·sigpanic if r->rip != 0.
- // If r->rip == 0, probably panicked because of a
- // call to a nil func. Not pushing that onto sp will
- // make the trace look like a call to runtime·sigpanic instead.
- // (Otherwise the trace will end at runtime·sigpanic and we
- // won't get to see who faulted.)
- if(r->Rip != 0) {
- sp = (uintptr*)r->Rsp;
- *--sp = r->Rip;
- r->Rsp = (uintptr)sp;
- }
- r->Rip = (uintptr)runtime·sigpanic;
- return -1;
+ return false;
+
+ if(!runtime·issigpanic(info->ExceptionCode))
+ return false;
+
+ return true;
+}
+
+// Called by sigtramp from Windows VEH handler.
+// Return value signals whether the exception has been handled (EXCEPTION_CONTINUE_EXECUTION)
+// or should be made available to other handlers in the chain (EXCEPTION_CONTINUE_SEARCH).
+uint32
+runtime·exceptionhandler(ExceptionRecord *info, Context *r, G *gp)
+{
+ uintptr *sp;
+
+ if(!runtime·isgoexception(info, r))
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ // Make it look like a call to the signal func.
+ // Have to pass arguments out of band since
+ // augmenting the stack frame would break
+ // the unwinding code.
+ gp->sig = info->ExceptionCode;
+ gp->sigcode0 = info->ExceptionInformation[0];
+ gp->sigcode1 = info->ExceptionInformation[1];
+ gp->sigpc = r->Rip;
+
+ // Only push runtime·sigpanic if r->rip != 0.
+ // If r->rip == 0, probably panicked because of a
+ // call to a nil func. Not pushing that onto sp will
+ // make the trace look like a call to runtime·sigpanic instead.
+ // (Otherwise the trace will end at runtime·sigpanic and we
+ // won't get to see who faulted.)
+ if(r->Rip != 0) {
+ sp = (uintptr*)r->Rsp;
+ *--sp = r->Rip;
+ r->Rsp = (uintptr)sp;
}
+ r->Rip = (uintptr)runtime·sigpanic;
+ return EXCEPTION_CONTINUE_EXECUTION;
+}
+
+// It seems Windows searches ContinueHandler's list even
+// if ExceptionHandler returns EXCEPTION_CONTINUE_EXECUTION.
+// firstcontinuehandler will stop that search,
+// if exceptionhandler did the same earlier.
+uint32
+runtime·firstcontinuehandler(ExceptionRecord *info, Context *r, G *gp)
+{
+ USED(gp);
+ if(!runtime·isgoexception(info, r))
+ return EXCEPTION_CONTINUE_SEARCH;
+ return EXCEPTION_CONTINUE_EXECUTION;
+}
+
+// lastcontinuehandler is reached, because runtime cannot handle
+// current exception. lastcontinuehandler will print crash info and exit.
+uint32
+runtime·lastcontinuehandler(ExceptionRecord *info, Context *r, G *gp)
+{
+ bool crash;
if(runtime·panicking) // traceback already printed
runtime·exit(2);
@@ -97,7 +128,7 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
runtime·crash();
runtime·exit(2);
- return -1; // not reached
+ return 0; // not reached
}
void
diff --git a/src/runtime/panic.c b/src/runtime/panic.c
index 1cd0aa865..24eb6dbfe 100644
--- a/src/runtime/panic.c
+++ b/src/runtime/panic.c
@@ -31,8 +31,11 @@ runtime·deferproc_m(void)
argp = g->m->scalararg[1];
callerpc = g->m->scalararg[2];
g->m->ptrarg[0] = nil;
+ g->m->scalararg[1] = 0;
d = runtime·newdefer(siz);
+ if(d->panic != nil)
+ runtime·throw("deferproc: d->panic != nil after newdefer");
d->fn = fn;
d->pc = callerpc;
d->argp = argp;
@@ -131,6 +134,7 @@ runtime·dopanic_m(void)
g->m->ptrarg[0] = nil;
pc = g->m->scalararg[0];
sp = g->m->scalararg[1];
+ g->m->scalararg[1] = 0;
if(gp->sig != 0)
runtime·printf("[signal %x code=%p addr=%p pc=%p]\n",
gp->sig, gp->sigcode0, gp->sigcode1, gp->sigpc);
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index 7eb2d6055..685ff5ca0 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -188,6 +188,12 @@ func newdefer(siz int32) *_defer {
// The defer cannot be used after this call.
//go:nosplit
func freedefer(d *_defer) {
+ if d._panic != nil {
+ freedeferpanic()
+ }
+ if d.fn != nil {
+ freedeferfn()
+ }
sc := deferclass(uintptr(d.siz))
if sc < uintptr(len(p{}.deferpool)) {
mp := acquirem()
@@ -199,6 +205,18 @@ func freedefer(d *_defer) {
}
}
+// Separate function so that it can split stack.
+// Windows otherwise runs out of stack space.
+func freedeferpanic() {
+ // _panic must be cleared before d is unlinked from gp.
+ gothrow("freedefer with d._panic != nil")
+}
+
+func freedeferfn() {
+ // fn must be cleared before d is unlinked from gp.
+ gothrow("freedefer with d.fn != nil")
+}
+
// Run a deferred function if there is one.
// The compiler inserts a call to this at the end of any
// function which calls defer.
@@ -231,6 +249,7 @@ func deferreturn(arg0 uintptr) {
mp := acquirem()
memmove(unsafe.Pointer(argp), deferArgs(d), uintptr(d.siz))
fn := d.fn
+ d.fn = nil
gp._defer = d.link
freedefer(d)
releasem(mp)
@@ -258,7 +277,9 @@ func Goexit() {
if d.started {
if d._panic != nil {
d._panic.aborted = true
+ d._panic = nil
}
+ d.fn = nil
gp._defer = d.link
freedefer(d)
continue
@@ -268,6 +289,8 @@ func Goexit() {
if gp._defer != d {
gothrow("bad defer entry in Goexit")
}
+ d._panic = nil
+ d.fn = nil
gp._defer = d.link
freedefer(d)
// Note: we ignore recovers here because Goexit isn't a panic
@@ -343,6 +366,8 @@ func gopanic(e interface{}) {
if d._panic != nil {
d._panic.aborted = true
}
+ d._panic = nil
+ d.fn = nil
gp._defer = d.link
freedefer(d)
continue
@@ -366,6 +391,8 @@ func gopanic(e interface{}) {
if gp._defer != d {
gothrow("bad defer entry in panic")
}
+ d._panic = nil
+ d.fn = nil
gp._defer = d.link
// trigger shrinkage to test stack copy. See stack_test.go:TestStackPanic
diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h
index 609ae9406..bea773799 100644
--- a/src/runtime/runtime.h
+++ b/src/runtime/runtime.h
@@ -372,7 +372,7 @@ struct M
uintptr scalararg[4]; // scalar argument/return for mcall
void* ptrarg[4]; // pointer argument/return for mcall
#ifdef GOOS_windows
- void* thread; // thread handle
+ uintptr thread; // thread handle
// these are here because they are too large to be on the stack
// of low-level NOSPLIT functions.
LibCall libcall;
@@ -865,6 +865,7 @@ void runtime·stackinit(void);
Stack runtime·stackalloc(uint32);
void runtime·stackfree(Stack);
void runtime·shrinkstack(G*);
+void runtime·shrinkfinish(void);
MCache* runtime·allocmcache(void);
void runtime·freemcache(MCache*);
void runtime·mallocinit(void);
@@ -898,7 +899,9 @@ void runtime·atomicstore(uint32 volatile*, uint32);
void runtime·atomicstore64(uint64 volatile*, uint64);
uint64 runtime·atomicload64(uint64 volatile*);
void* runtime·atomicloadp(void* volatile*);
+uintptr runtime·atomicloaduintptr(uintptr volatile*);
void runtime·atomicstorep(void* volatile*, void*);
+void runtime·atomicstoreuintptr(uintptr volatile*, uintptr);
void runtime·atomicor8(byte volatile*, byte);
void runtime·setg(G*);
diff --git a/src/runtime/stack.c b/src/runtime/stack.c
index 8562b9407..e402691f4 100644
--- a/src/runtime/stack.c
+++ b/src/runtime/stack.c
@@ -36,6 +36,8 @@ MSpan runtime·stackpool[NumStackOrders];
Mutex runtime·stackpoolmu;
// TODO: one lock per order?
+static Stack stackfreequeue;
+
void
runtime·stackinit(void)
{
@@ -656,7 +658,24 @@ copystack(G *gp, uintptr newsize)
while(p < ep)
*p++ = 0xfc;
}
- runtime·stackfree(old);
+ if(newsize > old.hi-old.lo) {
+ // growing, free stack immediately
+ runtime·stackfree(old);
+ } else {
+ // shrinking, queue up free operation. We can't actually free the stack
+ // just yet because we might run into the following situation:
+ // 1) GC starts, scans a SudoG but does not yet mark the SudoG.elem pointer
+ // 2) The stack that pointer points to is shrunk
+ // 3) The old stack is freed
+ // 4) The containing span is marked free
+ // 5) GC attempts to mark the SudoG.elem pointer. The marking fails because
+ // the pointer looks like a pointer into a free span.
+ // By not freeing, we prevent step #4 until GC is done.
+ runtime·lock(&runtime·stackpoolmu);
+ *(Stack*)old.lo = stackfreequeue;
+ stackfreequeue = old;
+ runtime·unlock(&runtime·stackpoolmu);
+ }
}
// round x up to a power of 2.
@@ -706,6 +725,7 @@ runtime·newstack(void)
g->m->morebuf.pc = (uintptr)nil;
g->m->morebuf.lr = (uintptr)nil;
g->m->morebuf.sp = (uintptr)nil;
+ g->m->morebuf.g = (G*)nil;
runtime·casgstatus(gp, Grunning, Gwaiting);
gp->waitreason = runtime·gostringnocopy((byte*)"stack growth");
@@ -841,6 +861,23 @@ runtime·shrinkstack(G *gp)
copystack(gp, newsize);
}
+// Do any delayed stack freeing that was queued up during GC.
+void
+runtime·shrinkfinish(void)
+{
+ Stack s, t;
+
+ runtime·lock(&runtime·stackpoolmu);
+ s = stackfreequeue;
+ stackfreequeue = (Stack){0,0};
+ runtime·unlock(&runtime·stackpoolmu);
+ while(s.lo != 0) {
+ t = *(Stack*)s.lo;
+ runtime·stackfree(s);
+ s = t;
+ }
+}
+
static void badc(void);
#pragma textflag NOSPLIT
diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go
index c6a9cf9f5..6561094ff 100644
--- a/src/runtime/stubs.go
+++ b/src/runtime/stubs.go
@@ -252,3 +252,33 @@ func return0()
// thunk to call time.now.
func timenow() (sec int64, nsec int32)
+
+// in asm_*.s
+// not called directly; definitions here supply type information for traceback.
+func call16(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call32(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call64(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call128(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call256(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call512(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call1024(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call2048(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call4096(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call8192(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call16384(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call32768(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call65536(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call131072(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call262144(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call524288(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call1048576(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call2097152(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call4194304(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call8388608(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call16777216(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call33554432(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call67108864(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call134217728(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call268435456(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call536870912(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call1073741824(fn, arg unsafe.Pointer, n, retoffset uint32)
diff --git a/src/runtime/sys_windows_386.s b/src/runtime/sys_windows_386.s
index 1bf4d062a..932fe9dd2 100644
--- a/src/runtime/sys_windows_386.s
+++ b/src/runtime/sys_windows_386.s
@@ -73,6 +73,7 @@ TEXT runtime·setlasterror(SB),NOSPLIT,$0
// Called by Windows as a Vectored Exception Handler (VEH).
// First argument is pointer to struct containing
// exception record and context pointers.
+// Handler function is stored in AX.
// Return 0 for 'not handled', -1 for handled.
TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
MOVL ptrs+0(FP), CX
@@ -84,6 +85,8 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
MOVL SI, 20(SP)
MOVL DI, 24(SP)
+ MOVL AX, SI // save handler address
+
// find g
get_tls(DX)
CMPL DX, $0
@@ -123,11 +126,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
sigtramp_g0:
MOVL 0(CX), BX // ExceptionRecord*
MOVL 4(CX), CX // Context*
- // call sighandler(ExceptionRecord*, Context*, G*)
MOVL BX, 0(SP)
MOVL CX, 4(SP)
MOVL DX, 8(SP)
- CALL runtime·sighandler(SB)
+ CALL SI // call handler
// AX is set to report result back to Windows
MOVL 12(SP), AX
@@ -149,6 +151,18 @@ done:
// RET 4 (return and pop 4 bytes parameters)
BYTE $0xC2; WORD $4
RET // unreached; make assembler happy
+
+TEXT runtime·exceptiontramp(SB),NOSPLIT,$0
+ MOVL $runtime·exceptionhandler(SB), AX
+ JMP runtime·sigtramp(SB)
+
+TEXT runtime·firstcontinuetramp(SB),NOSPLIT,$0-0
+ // is never called
+ INT $3
+
+TEXT runtime·lastcontinuetramp(SB),NOSPLIT,$0-0
+ MOVL $runtime·lastcontinuehandler(SB), AX
+ JMP runtime·sigtramp(SB)
TEXT runtime·ctrlhandler(SB),NOSPLIT,$0
PUSHL $runtime·ctrlhandler1(SB)
diff --git a/src/runtime/sys_windows_amd64.s b/src/runtime/sys_windows_amd64.s
index 05750398e..e6190ce68 100644
--- a/src/runtime/sys_windows_amd64.s
+++ b/src/runtime/sys_windows_amd64.s
@@ -99,6 +99,7 @@ TEXT runtime·setlasterror(SB),NOSPLIT,$0
// Called by Windows as a Vectored Exception Handler (VEH).
// First argument is pointer to struct containing
// exception record and context pointers.
+// Handler function is stored in AX.
// Return 0 for 'not handled', -1 for handled.
TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
// CX: PEXCEPTION_POINTERS ExceptionInfo
@@ -116,6 +117,8 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
MOVQ R14, 32(SP)
MOVQ R15, 88(SP)
+ MOVQ AX, R15 // save handler address
+
// find g
get_tls(DX)
CMPQ DX, $0
@@ -157,11 +160,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
sigtramp_g0:
MOVQ 0(CX), BX // ExceptionRecord*
MOVQ 8(CX), CX // Context*
- // call sighandler(ExceptionRecord*, Context*, G*)
MOVQ BX, 0(SP)
MOVQ CX, 8(SP)
MOVQ DX, 16(SP)
- CALL runtime·sighandler(SB)
+ CALL R15 // call handler
// AX is set to report result back to Windows
MOVL 24(SP), AX
@@ -187,6 +189,18 @@ done:
RET
+TEXT runtime·exceptiontramp(SB),NOSPLIT,$0
+ MOVQ $runtime·exceptionhandler(SB), AX
+ JMP runtime·sigtramp(SB)
+
+TEXT runtime·firstcontinuetramp(SB),NOSPLIT,$0-0
+ MOVQ $runtime·firstcontinuehandler(SB), AX
+ JMP runtime·sigtramp(SB)
+
+TEXT runtime·lastcontinuetramp(SB),NOSPLIT,$0-0
+ MOVQ $runtime·lastcontinuehandler(SB), AX
+ JMP runtime·sigtramp(SB)
+
TEXT runtime·ctrlhandler(SB),NOSPLIT,$8
MOVQ CX, 16(SP) // spill
MOVQ $runtime·ctrlhandler1(SB), CX
diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go
index 9ed016ccc..ce8a9ec1b 100644
--- a/src/runtime/syscall_windows_test.go
+++ b/src/runtime/syscall_windows_test.go
@@ -494,3 +494,42 @@ func TestOutputDebugString(t *testing.T) {
p := syscall.StringToUTF16Ptr("testing OutputDebugString")
d.Proc("OutputDebugStringW").Call(uintptr(unsafe.Pointer(p)))
}
+
+func TestRaiseException(t *testing.T) {
+ o := executeTest(t, raiseExceptionSource, nil)
+ if strings.Contains(o, "RaiseException should not return") {
+ t.Fatalf("RaiseException did not crash program: %v", o)
+ }
+ if !strings.Contains(o, "Exception 0xbad") {
+ t.Fatalf("No stack trace: %v", o)
+ }
+}
+
+const raiseExceptionSource = `
+package main
+import "syscall"
+func main() {
+ const EXCEPTION_NONCONTINUABLE = 1
+ mod := syscall.MustLoadDLL("kernel32.dll")
+ proc := mod.MustFindProc("RaiseException")
+ proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0)
+ println("RaiseException should not return")
+}
+`
+
+func TestZeroDivisionException(t *testing.T) {
+ o := executeTest(t, zeroDivisionExceptionSource, nil)
+ if !strings.Contains(o, "panic: runtime error: integer divide by zero") {
+ t.Fatalf("No stack trace: %v", o)
+ }
+}
+
+const zeroDivisionExceptionSource = `
+package main
+func main() {
+ x := 1
+ y := 0
+ z := x / y
+ println(z)
+}
+`
diff --git a/src/runtime/type.h b/src/runtime/type.h
index de82e886f..f5b4f9d13 100644
--- a/src/runtime/type.h
+++ b/src/runtime/type.h
@@ -23,7 +23,7 @@ struct Type
uint8 kind;
void* alg;
// gc stores type info required for garbage collector.
- // If (kind&KindGCProg)==0, then gc directly contains sparse GC bitmap
+ // If (kind&KindGCProg)==0, then gc[0] points at sparse GC bitmap
// (no indirection), 4 bits per word.
// If (kind&KindGCProg)!=0, then gc[1] points to a compiler-generated
// read-only GC program; and gc[0] points to BSS space for sparse GC bitmap.
diff --git a/src/strings/strings.go b/src/strings/strings.go
index 1b9df2e75..27d384983 100644
--- a/src/strings/strings.go
+++ b/src/strings/strings.go
@@ -225,13 +225,8 @@ func LastIndex(s, sep string) int {
// r, or -1 if rune is not present in s.
func IndexRune(s string, r rune) int {
switch {
- case r < 0x80:
- b := byte(r)
- for i := 0; i < len(s); i++ {
- if s[i] == b {
- return i
- }
- }
+ case r < utf8.RuneSelf:
+ return IndexByte(s, byte(r))
default:
for i, c := range s {
if c == r {
diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go
index bda8214c3..e89fd096a 100644
--- a/src/syscall/syscall_windows.go
+++ b/src/syscall/syscall_windows.go
@@ -468,7 +468,7 @@ func Utimes(path string, tv []Timeval) (err error) {
}
h, e := CreateFile(pathp,
FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE, nil,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)
if e != nil {
return e
}
@@ -488,7 +488,7 @@ func UtimesNano(path string, ts []Timespec) (err error) {
}
h, e := CreateFile(pathp,
FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE, nil,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)
if e != nil {
return e
}
diff --git a/src/syscall/ztypes_windows.go b/src/syscall/ztypes_windows.go
index 1363da01a..4c8a99ab9 100644
--- a/src/syscall/ztypes_windows.go
+++ b/src/syscall/ztypes_windows.go
@@ -547,6 +547,7 @@ const (
IOC_WS2 = 0x08000000
SIO_GET_EXTENSION_FUNCTION_POINTER = IOC_INOUT | IOC_WS2 | 6
SIO_KEEPALIVE_VALS = IOC_IN | IOC_VENDOR | 4
+ SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12
// cf. http://support.microsoft.com/default.aspx?scid=kb;en-us;257460