summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/all.bash4
-rw-r--r--src/all.bat2
-rw-r--r--src/cmd/5c/list.c4
-rw-r--r--src/cmd/5g/gobj.c3
-rw-r--r--src/cmd/5g/list.c4
-rw-r--r--src/cmd/5g/reg.c4
-rw-r--r--src/cmd/5l/list.c4
-rw-r--r--src/cmd/6g/gobj.c3
-rw-r--r--src/cmd/6g/reg.c4
-rw-r--r--src/cmd/8g/gobj.c3
-rw-r--r--src/cmd/8g/reg.c4
-rw-r--r--src/cmd/api/goapi.go164
-rw-r--r--src/cmd/cc/scon.c5
-rw-r--r--src/cmd/cgo/main.go16
-rw-r--r--src/cmd/cgo/out.go72
-rw-r--r--src/cmd/dist/build.c9
-rw-r--r--src/cmd/gc/dcl.c17
-rw-r--r--src/cmd/gc/doc.go4
-rw-r--r--src/cmd/gc/gen.c3
-rw-r--r--src/cmd/gc/go.h6
-rw-r--r--src/cmd/gc/inl.c43
-rw-r--r--src/cmd/gc/lex.c1
-rw-r--r--src/cmd/gc/range.c11
-rw-r--r--src/cmd/gc/reflect.c2
-rw-r--r--src/cmd/gc/subr.c10
-rw-r--r--src/cmd/gc/typecheck.c119
-rw-r--r--src/cmd/gc/walk.c2
-rw-r--r--src/cmd/go/build.go5
-rw-r--r--src/cmd/go/doc.go17
-rw-r--r--src/cmd/go/fmt.go21
-rw-r--r--src/cmd/go/help.go4
-rw-r--r--src/cmd/go/main.go30
-rw-r--r--src/cmd/go/run.go4
-rw-r--r--src/cmd/go/vet.go9
-rw-r--r--src/lib9/ctime.c4
-rw-r--r--src/pkg/compress/flate/deflate.go20
-rw-r--r--src/pkg/compress/flate/deflate_test.go44
-rw-r--r--src/pkg/crypto/aes/const.go4
-rw-r--r--src/pkg/crypto/ecdsa/ecdsa.go6
-rw-r--r--src/pkg/crypto/rsa/pkcs1v15.go1
-rw-r--r--src/pkg/crypto/tls/handshake_messages.go4
-rw-r--r--src/pkg/crypto/x509/x509.go4
-rw-r--r--src/pkg/debug/gosym/pclntab_test.go18
-rw-r--r--src/pkg/encoding/base64/base64.go2
-rw-r--r--src/pkg/encoding/base64/base64_test.go49
-rw-r--r--src/pkg/encoding/gob/doc.go2
-rw-r--r--src/pkg/encoding/json/decode.go14
-rw-r--r--src/pkg/encoding/json/decode_test.go65
-rw-r--r--src/pkg/encoding/json/encode.go4
-rw-r--r--src/pkg/flag/flag.go5
-rw-r--r--src/pkg/fmt/doc.go2
-rw-r--r--src/pkg/go/ast/ast.go14
-rw-r--r--src/pkg/go/ast/ast_test.go50
-rw-r--r--src/pkg/go/build/build.go4
-rw-r--r--src/pkg/go/parser/parser.go42
-rw-r--r--src/pkg/go/parser/parser_test.go195
-rw-r--r--src/pkg/go/printer/nodes.go15
-rw-r--r--src/pkg/log/log.go2
-rw-r--r--src/pkg/math/big/nat.go10
-rw-r--r--src/pkg/math/big/nat_test.go18
-rw-r--r--src/pkg/mime/multipart/multipart.go115
-rw-r--r--src/pkg/mime/multipart/multipart_test.go245
-rw-r--r--src/pkg/net/file.go4
-rw-r--r--src/pkg/net/http/client.go5
-rw-r--r--src/pkg/net/http/client_test.go25
-rw-r--r--src/pkg/net/http/proxy_test.go30
-rw-r--r--src/pkg/net/http/response.go9
-rw-r--r--src/pkg/net/http/response_test.go15
-rw-r--r--src/pkg/net/http/server.go2
-rw-r--r--src/pkg/net/http/transfer.go6
-rw-r--r--src/pkg/net/http/transport.go6
-rw-r--r--src/pkg/net/mail/message.go11
-rw-r--r--src/pkg/net/mail/message_test.go5
-rw-r--r--src/pkg/net/url/url.go5
-rw-r--r--src/pkg/net/url/url_test.go31
-rw-r--r--src/pkg/os/exec/exec.go18
-rw-r--r--src/pkg/path/filepath/path.go7
-rw-r--r--src/pkg/path/filepath/path_plan9.go2
-rw-r--r--src/pkg/path/filepath/path_test.go26
-rw-r--r--src/pkg/regexp/regexp.go4
-rw-r--r--src/pkg/regexp/syntax/parse.go7
-rw-r--r--src/pkg/regexp/syntax/parse_test.go8
-rw-r--r--src/pkg/runtime/alg.c28
-rw-r--r--src/pkg/runtime/extern.go5
-rw-r--r--src/pkg/runtime/hashmap.c161
-rw-r--r--src/pkg/runtime/malloc.goc1
-rw-r--r--src/pkg/runtime/mfinal.c3
-rw-r--r--src/pkg/runtime/mgc0.c1
-rw-r--r--src/pkg/runtime/softfloat_arm.c65
-rw-r--r--src/pkg/runtime/time.goc5
-rw-r--r--src/pkg/runtime/vlrt_arm.c2
-rw-r--r--src/pkg/strconv/itoa.go8
-rw-r--r--src/pkg/strings/example_test.go1
-rw-r--r--src/pkg/syscall/syscall_linux_arm.go30
-rw-r--r--src/pkg/syscall/syscall_windows.go39
-rw-r--r--src/pkg/syscall/syscall_windows_test.go50
-rw-r--r--src/pkg/syscall/zsyscall_linux_arm.go28
-rw-r--r--src/pkg/syscall/zsyscall_windows_386.go11
-rw-r--r--src/pkg/syscall/zsyscall_windows_amd64.go11
-rw-r--r--src/pkg/syscall/ztypes_windows.go32
-rw-r--r--src/pkg/text/template/exec.go7
-rw-r--r--src/pkg/text/template/exec_test.go6
-rw-r--r--src/pkg/time/sleep_test.go22
-rw-r--r--src/pkg/unicode/maketables.go4
-rw-r--r--src/pkg/unicode/tables.go6
-rwxr-xr-xsrc/run.bash7
-rw-r--r--src/run.bat7
107 files changed, 1823 insertions, 514 deletions
diff --git a/src/all.bash b/src/all.bash
index 932b65dc0..488ca4679 100755
--- a/src/all.bash
+++ b/src/all.bash
@@ -8,6 +8,8 @@ if [ ! -f make.bash ]; then
echo 'all.bash must be run from $GOROOT/src' 1>&2
exit 1
fi
+OLDPATH="$PATH"
. ./make.bash --no-banner
-bash run.bash --no-rebuild --banner
+bash run.bash --no-rebuild
+PATH="$OLDPATH"
$GOTOOLDIR/dist banner # print build info
diff --git a/src/all.bat b/src/all.bat
index e3b61c012..8edfd7f43 100644
--- a/src/all.bat
+++ b/src/all.bat
@@ -11,10 +11,12 @@ echo all.bat must be run from go\src
goto end
:ok
+set OLDPATH=%PATH%
call make.bat --no-banner --no-local
if %GOBUILDFAIL%==1 goto end
call run.bat --no-rebuild --no-local
if %GOBUILDFAIL%==1 goto end
+set PATH=%OLDPATH%
go tool dist banner
:end
diff --git a/src/cmd/5c/list.c b/src/cmd/5c/list.c
index ab0fae83c..30b88400f 100644
--- a/src/cmd/5c/list.c
+++ b/src/cmd/5c/list.c
@@ -140,7 +140,7 @@ Dconv(Fmt *fp)
{
char str[STRINGSZ];
Adr *a;
- char *op;
+ const char *op;
int v;
a = va_arg(fp->args, Adr*);
@@ -169,7 +169,7 @@ Dconv(Fmt *fp)
case D_SHIFT:
v = a->offset;
- op = "<<>>->@>" + (((v>>5) & 3) << 1);
+ op = &"<<>>->@>"[(((v>>5) & 3) << 1)];
if(v & (1<<4))
sprint(str, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15);
else
diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c
index b562ba888..2763e7b16 100644
--- a/src/cmd/5g/gobj.c
+++ b/src/cmd/5g/gobj.c
@@ -198,7 +198,8 @@ dumpfuncs(void)
if(isblank(pl->name))
continue;
- if(debug['S']) {
+ // -S prints code; -SS prints code and data
+ if(debug['S'] && (pl->name || debug['S']>1)) {
s = S;
if(pl->name != N)
s = pl->name->sym;
diff --git a/src/cmd/5g/list.c b/src/cmd/5g/list.c
index 9bc3a9a9a..f0da24742 100644
--- a/src/cmd/5g/list.c
+++ b/src/cmd/5g/list.c
@@ -83,7 +83,7 @@ int
Dconv(Fmt *fp)
{
char str[STRINGSZ];
- char *op;
+ const char *op;
Addr *a;
int i;
int32 v;
@@ -119,7 +119,7 @@ Dconv(Fmt *fp)
case D_SHIFT:
v = a->offset;
- op = "<<>>->@>" + (((v>>5) & 3) << 1);
+ op = &"<<>>->@>"[(((v>>5) & 3) << 1)];
if(v & (1<<4))
sprint(str, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15);
else
diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c
index 93724d032..393266973 100644
--- a/src/cmd/5g/reg.c
+++ b/src/cmd/5g/reg.c
@@ -695,8 +695,8 @@ brk:
}
}
}
- if(r1 != R) {
- r1->link = freer;
+ if(lastr != R) {
+ lastr->link = freer;
freer = firstr;
}
diff --git a/src/cmd/5l/list.c b/src/cmd/5l/list.c
index 7b623d78a..293fee3c6 100644
--- a/src/cmd/5l/list.c
+++ b/src/cmd/5l/list.c
@@ -161,7 +161,7 @@ int
Dconv(Fmt *fp)
{
char str[STRINGSZ];
- char *op;
+ const char *op;
Adr *a;
int32 v;
@@ -191,7 +191,7 @@ Dconv(Fmt *fp)
case D_SHIFT:
v = a->offset;
- op = "<<>>->@>" + (((v>>5) & 3) << 1);
+ op = &"<<>>->@>"[(((v>>5) & 3) << 1)];
if(v & (1<<4))
snprint(str, sizeof str, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15);
else
diff --git a/src/cmd/6g/gobj.c b/src/cmd/6g/gobj.c
index 80de2f750..8c9208374 100644
--- a/src/cmd/6g/gobj.c
+++ b/src/cmd/6g/gobj.c
@@ -244,7 +244,8 @@ dumpfuncs(void)
if(isblank(pl->name))
continue;
- if(debug['S']) {
+ // -S prints code; -SS prints code and data
+ if(debug['S'] && (pl->name || debug['S']>1)) {
s = S;
if(pl->name != N)
s = pl->name->sym;
diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c
index bed9f8da6..049c63f17 100644
--- a/src/cmd/6g/reg.c
+++ b/src/cmd/6g/reg.c
@@ -780,8 +780,8 @@ brk:
p->to.branch = p->to.branch->link;
}
- if(r1 != R) {
- r1->link = freer;
+ if(lastr != R) {
+ lastr->link = freer;
freer = firstr;
}
diff --git a/src/cmd/8g/gobj.c b/src/cmd/8g/gobj.c
index d8c8f5ab9..da0055cd9 100644
--- a/src/cmd/8g/gobj.c
+++ b/src/cmd/8g/gobj.c
@@ -242,7 +242,8 @@ dumpfuncs(void)
if(isblank(pl->name))
continue;
- if(debug['S']) {
+ // -S prints code; -SS prints code and data
+ if(debug['S'] && (pl->name || debug['S']>1)) {
s = S;
if(pl->name != N)
s = pl->name->sym;
diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c
index 29270c820..45ffdf96c 100644
--- a/src/cmd/8g/reg.c
+++ b/src/cmd/8g/reg.c
@@ -677,8 +677,8 @@ brk:
p->to.branch = p->to.branch->link;
}
- if(r1 != R) {
- r1->link = freer;
+ if(lastr != R) {
+ lastr->link = freer;
freer = firstr;
}
diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go
index 7363f6d82..ad1c6bb8c 100644
--- a/src/cmd/api/goapi.go
+++ b/src/cmd/api/goapi.go
@@ -28,6 +28,7 @@ import (
"os/exec"
"path"
"path/filepath"
+ "runtime"
"sort"
"strconv"
"strings"
@@ -35,27 +36,31 @@ import (
// Flags
var (
+ // TODO(bradfitz): once Go 1.1 comes out, allow the -c flag to take a comma-separated
+ // list of files, rather than just one.
checkFile = flag.String("c", "", "optional filename to check API against")
- verbose = flag.Bool("v", false, "Verbose debugging")
+ allowNew = flag.Bool("allow_new", true, "allow API additions")
+ nextFile = flag.String("next", "", "optional filename of tentative upcoming API features for the next release. This file can be lazily maintained. It only affects the delta warnings from the -c file printed on success.")
+ verbose = flag.Bool("v", false, "verbose debugging")
+ forceCtx = flag.String("contexts", "", "optional comma-separated list of <goos>-<goarch>[-cgo] to override default contexts.")
)
+// contexts are the default contexts which are scanned, unless
+// overridden by the -contexts flag.
var contexts = []*build.Context{
{GOOS: "linux", GOARCH: "386", CgoEnabled: true},
{GOOS: "linux", GOARCH: "386"},
{GOOS: "linux", GOARCH: "amd64", CgoEnabled: true},
{GOOS: "linux", GOARCH: "amd64"},
+ {GOOS: "linux", GOARCH: "arm"},
{GOOS: "darwin", GOARCH: "386", CgoEnabled: true},
{GOOS: "darwin", GOARCH: "386"},
{GOOS: "darwin", GOARCH: "amd64", CgoEnabled: true},
{GOOS: "darwin", GOARCH: "amd64"},
{GOOS: "windows", GOARCH: "amd64"},
{GOOS: "windows", GOARCH: "386"},
-}
-
-func init() {
- for _, c := range contexts {
- c.Compiler = build.Default.Compiler
- }
+ {GOOS: "freebsd", GOARCH: "amd64"},
+ {GOOS: "freebsd", GOARCH: "386"},
}
func contextName(c *build.Context) string {
@@ -66,9 +71,49 @@ func contextName(c *build.Context) string {
return s
}
+func parseContext(c string) *build.Context {
+ parts := strings.Split(c, "-")
+ if len(parts) < 2 {
+ log.Fatalf("bad context: %q", c)
+ }
+ bc := &build.Context{
+ GOOS: parts[0],
+ GOARCH: parts[1],
+ }
+ if len(parts) == 3 {
+ if parts[2] == "cgo" {
+ bc.CgoEnabled = true
+ } else {
+ log.Fatalf("bad context: %q", c)
+ }
+ }
+ return bc
+}
+
+func setContexts() {
+ contexts = []*build.Context{}
+ for _, c := range strings.Split(*forceCtx, ",") {
+ contexts = append(contexts, parseContext(c))
+ }
+}
+
func main() {
flag.Parse()
+ if !strings.Contains(runtime.Version(), "weekly") {
+ if *nextFile != "" {
+ fmt.Printf("Go version is %q, ignoring -next %s\n", runtime.Version(), *nextFile)
+ *nextFile = ""
+ }
+ }
+
+ if *forceCtx != "" {
+ setContexts()
+ }
+ for _, c := range contexts {
+ c.Compiler = build.Default.Compiler
+ }
+
var pkgs []string
if flag.NArg() > 0 {
pkgs = flag.Args()
@@ -123,45 +168,86 @@ func main() {
}
sort.Strings(features)
+ fail := false
+ defer func() {
+ if fail {
+ os.Exit(1)
+ }
+ }()
+
bw := bufio.NewWriter(os.Stdout)
defer bw.Flush()
- if *checkFile != "" {
- bs, err := ioutil.ReadFile(*checkFile)
- if err != nil {
- log.Fatalf("Error reading file %s: %v", *checkFile, err)
- }
- v1 := strings.Split(strings.TrimSpace(string(bs)), "\n")
- sort.Strings(v1)
- v2 := features
- take := func(sl *[]string) string {
- s := (*sl)[0]
- *sl = (*sl)[1:]
- return s
- }
- changes := false
- for len(v1) > 0 || len(v2) > 0 {
- switch {
- case len(v2) == 0 || v1[0] < v2[0]:
- fmt.Fprintf(bw, "-%s\n", take(&v1))
- changes = true
- case len(v1) == 0 || v1[0] > v2[0]:
- fmt.Fprintf(bw, "+%s\n", take(&v2))
- changes = true
- default:
- take(&v1)
- take(&v2)
- }
- }
- if changes {
- bw.Flush()
- os.Exit(1)
- }
- } else {
+ if *checkFile == "" {
for _, f := range features {
fmt.Fprintf(bw, "%s\n", f)
}
+ return
+ }
+
+ var required []string
+ for _, filename := range []string{*checkFile} {
+ required = append(required, fileFeatures(filename)...)
+ }
+ sort.Strings(required)
+
+ var optional = make(map[string]bool) // feature => true
+ if *nextFile != "" {
+ for _, feature := range fileFeatures(*nextFile) {
+ optional[feature] = true
+ }
+ }
+
+ take := func(sl *[]string) string {
+ s := (*sl)[0]
+ *sl = (*sl)[1:]
+ return s
+ }
+
+ for len(required) > 0 || len(features) > 0 {
+ switch {
+ case len(features) == 0 || required[0] < features[0]:
+ fmt.Fprintf(bw, "-%s\n", take(&required))
+ fail = true // broke compatibility
+ case len(required) == 0 || required[0] > features[0]:
+ newFeature := take(&features)
+ if optional[newFeature] {
+ // Known added feature to the upcoming release.
+ // Delete it from the map so we can detect any upcoming features
+ // which were never seen. (so we can clean up the nextFile)
+ delete(optional, newFeature)
+ } else {
+ fmt.Fprintf(bw, "+%s\n", newFeature)
+ if !*allowNew {
+ fail = true // we're in lock-down mode for next release
+ }
+ }
+ default:
+ take(&required)
+ take(&features)
+ }
+ }
+
+ var missing []string
+ for feature := range optional {
+ missing = append(missing, feature)
+ }
+ sort.Strings(missing)
+ for _, feature := range missing {
+ fmt.Fprintf(bw, "(in next file, but not in API) -%s\n", feature)
+ }
+}
+
+func fileFeatures(filename string) []string {
+ bs, err := ioutil.ReadFile(filename)
+ if err != nil {
+ log.Fatalf("Error reading file %s: %v", filename, err)
+ }
+ text := strings.TrimSpace(string(bs))
+ if text == "" {
+ return nil
}
+ return strings.Split(text, "\n")
}
// pkgSymbol represents a symbol in a package
diff --git a/src/cmd/cc/scon.c b/src/cmd/cc/scon.c
index 193331f77..f6031a5be 100644
--- a/src/cmd/cc/scon.c
+++ b/src/cmd/cc/scon.c
@@ -175,7 +175,10 @@ evconst(Node *n)
break;
case OLSHR:
- v = (uvlong)l->vconst >> r->vconst;
+ if(l->type->width != sizeof(uvlong))
+ v = ((uvlong)l->vconst & 0xffffffffULL) >> r->vconst;
+ else
+ v = (uvlong)l->vconst >> r->vconst;
break;
case OASHR:
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
index 7449f04c4..60165961a 100644
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -22,6 +22,7 @@ import (
"path/filepath"
"reflect"
"runtime"
+ "sort"
"strings"
)
@@ -33,9 +34,8 @@ type Package struct {
GccOptions []string
CgoFlags map[string]string // #cgo flags (CFLAGS, LDFLAGS)
Written map[string]bool
- Name map[string]*Name // accumulated Name from Files
- Typedef map[string]ast.Expr // accumulated Typedef from Files
- ExpFunc []*ExpFunc // accumulated ExpFunc from Files
+ Name map[string]*Name // accumulated Name from Files
+ ExpFunc []*ExpFunc // accumulated ExpFunc from Files
Decl []ast.Decl
GoFiles []string // list of Go files
GccFiles []string // list of gcc output files
@@ -51,7 +51,15 @@ type File struct {
Ref []*Ref // all references to C.xxx in AST
ExpFunc []*ExpFunc // exported functions for this file
Name map[string]*Name // map from Go name to Name
- Typedef map[string]ast.Expr // translations of all necessary types from C
+}
+
+func nameKeys(m map[string]*Name) []string {
+ var ks []string
+ for k := range m {
+ ks = append(ks, k)
+ }
+ sort.Strings(ks)
+ return ks
}
// A Ref refers to an expression of the form C.xxx in the AST.
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 814250c2e..5dfc16a02 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -71,7 +71,8 @@ func (p *Package) writeDefs() {
}
cVars := make(map[string]bool)
- for _, n := range p.Name {
+ for _, key := range nameKeys(p.Name) {
+ n := p.Name[key]
if n.Kind != "var" {
continue
}
@@ -94,14 +95,16 @@ func (p *Package) writeDefs() {
}
fmt.Fprintf(fc, "\n")
- for _, n := range p.Name {
+ for _, key := range nameKeys(p.Name) {
+ n := p.Name[key]
if n.Const != "" {
fmt.Fprintf(fgo2, "const _Cconst_%s = %s\n", n.Go, n.Const)
}
}
fmt.Fprintf(fgo2, "\n")
- for _, n := range p.Name {
+ for _, key := range nameKeys(p.Name) {
+ n := p.Name[key]
if n.FuncType != nil {
p.writeDefsFunc(fc, fgo2, n)
}
@@ -372,7 +375,8 @@ func (p *Package) writeOutput(f *File, srcfile string) {
fmt.Fprintf(fgcc, "%s\n", f.Preamble)
fmt.Fprintf(fgcc, "%s\n", gccProlog)
- for _, n := range f.Name {
+ for _, key := range nameKeys(f.Name) {
+ n := f.Name[key]
if n.FuncType != nil {
p.writeOutputFunc(fgcc, n)
}
@@ -736,25 +740,23 @@ func c(repr string, args ...interface{}) *TypeRepr {
// Map predeclared Go types to Type.
var goTypes = map[string]*Type{
- "bool": {Size: 1, Align: 1, C: c("uchar")},
- "byte": {Size: 1, Align: 1, C: c("uchar")},
- "int": {Size: 4, Align: 4, C: c("int")},
- "uint": {Size: 4, Align: 4, C: c("uint")},
- "rune": {Size: 4, Align: 4, C: c("int")},
- "int8": {Size: 1, Align: 1, C: c("schar")},
- "uint8": {Size: 1, Align: 1, C: c("uchar")},
- "int16": {Size: 2, Align: 2, C: c("short")},
- "uint16": {Size: 2, Align: 2, C: c("ushort")},
- "int32": {Size: 4, Align: 4, C: c("int")},
- "uint32": {Size: 4, Align: 4, C: c("uint")},
- "int64": {Size: 8, Align: 8, C: c("int64")},
- "uint64": {Size: 8, Align: 8, C: c("uint64")},
- "float": {Size: 4, Align: 4, C: c("float")},
- "float32": {Size: 4, Align: 4, C: c("float")},
- "float64": {Size: 8, Align: 8, C: c("double")},
- "complex": {Size: 8, Align: 8, C: c("__complex float")},
- "complex64": {Size: 8, Align: 8, C: c("__complex float")},
- "complex128": {Size: 16, Align: 16, C: c("__complex double")},
+ "bool": {Size: 1, Align: 1, C: c("GoUint8")},
+ "byte": {Size: 1, Align: 1, C: c("GoUint8")},
+ "int": {Size: 4, Align: 4, C: c("GoInt")},
+ "uint": {Size: 4, Align: 4, C: c("GoUint")},
+ "rune": {Size: 4, Align: 4, C: c("GoInt32")},
+ "int8": {Size: 1, Align: 1, C: c("GoInt8")},
+ "uint8": {Size: 1, Align: 1, C: c("GoUint8")},
+ "int16": {Size: 2, Align: 2, C: c("GoInt16")},
+ "uint16": {Size: 2, Align: 2, C: c("GoUint16")},
+ "int32": {Size: 4, Align: 4, C: c("GoInt32")},
+ "uint32": {Size: 4, Align: 4, C: c("GoUint32")},
+ "int64": {Size: 8, Align: 8, C: c("GoInt64")},
+ "uint64": {Size: 8, Align: 8, C: c("GoUint64")},
+ "float32": {Size: 4, Align: 4, C: c("GoFloat32")},
+ "float64": {Size: 8, Align: 8, C: c("GoFloat64")},
+ "complex64": {Size: 8, Align: 8, C: c("GoComplex64")},
+ "complex128": {Size: 16, Align: 16, C: c("GoComplex128")},
}
// Map an ast type to a Type.
@@ -799,7 +801,7 @@ func (p *Package) cgoType(e ast.Expr) *Type {
return def
}
if t.Name == "uintptr" {
- return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("uintptr")}
+ return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoUintptr")}
}
if t.Name == "string" {
return &Type{Size: p.PtrSize + 4, Align: p.PtrSize, C: c("GoString")}
@@ -930,13 +932,21 @@ Slice GoBytes(char *p, int n) {
`
const gccExportHeaderProlog = `
-typedef unsigned int uint;
-typedef signed char schar;
-typedef unsigned char uchar;
-typedef unsigned short ushort;
-typedef long long int64;
-typedef unsigned long long uint64;
-typedef __SIZE_TYPE__ uintptr;
+typedef int GoInt;
+typedef unsigned int GoUint;
+typedef signed char GoInt8;
+typedef unsigned char GoUint8;
+typedef short GoInt16;
+typedef unsigned short GoUint16;
+typedef int GoInt32;
+typedef unsigned int GoUint32;
+typedef long long GoInt64;
+typedef unsigned long long GoUint64;
+typedef __SIZE_TYPE__ GoUintptr;
+typedef float GoFloat32;
+typedef double GoFloat64;
+typedef __complex float GoComplex64;
+typedef __complex double GoComplex128;
typedef struct { char *p; int n; } GoString;
typedef void *GoMap;
diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c
index 3ef65f85d..7f9aa7bcd 100644
--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -1230,6 +1230,15 @@ clean(void)
xremove(bpathf(&b, "%s/%s", bstr(&path), cleantab[i]+4));
}
+ // remove src/pkg/runtime/z* unconditionally
+ vreset(&dir);
+ bpathf(&path, "%s/src/pkg/runtime", goroot);
+ xreaddir(&dir, bstr(&path));
+ for(j=0; j<dir.len; j++) {
+ if(hasprefix(dir.p[j], "z"))
+ xremove(bpathf(&b, "%s/%s", bstr(&path), dir.p[j]));
+ }
+
if(rebuildall) {
// Remove object tree.
xremoveall(bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch));
diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c
index 4121a45ab..32f334b71 100644
--- a/src/cmd/gc/dcl.c
+++ b/src/cmd/gc/dcl.c
@@ -433,21 +433,6 @@ oldname(Sym *s)
}
/*
- * same for types
- */
-Type*
-newtype(Sym *s)
-{
- Type *t;
-
- t = typ(TFORW);
- t->sym = s;
- t->type = T;
- return t;
-}
-
-
-/*
* := declarations
*/
@@ -1311,7 +1296,7 @@ addmethod(Sym *sf, Type *t, int local)
}
// Should have picked off all the reasons above,
// but just in case, fall back to generic error.
- yyerror("invalid receiver type %T", pa);
+ yyerror("invalid receiver type %T (%lT / %lT)", pa, pa, t);
return;
}
diff --git a/src/cmd/gc/doc.go b/src/cmd/gc/doc.go
index 163d3862c..8d8f8967b 100644
--- a/src/cmd/gc/doc.go
+++ b/src/cmd/gc/doc.go
@@ -47,7 +47,9 @@ Flags:
-N
disable optimizations
-S
- write assembly language text to standard output
+ write assembly language text to standard output (code only)
+ -SS
+ write assembly language text to standard output (code and data)
-u
disallow importing packages not marked as safe
-V
diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c
index 694a10ab5..96e7b526c 100644
--- a/src/cmd/gc/gen.c
+++ b/src/cmd/gc/gen.c
@@ -647,6 +647,9 @@ cgen_as(Node *nl, Node *nr)
dump("cgen_as = ", nr);
}
+ while(nr != N && nr->op == OCONVNOP)
+ nr = nr->left;
+
if(nl == N || isblank(nl)) {
cgen_discard(nr);
return;
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index 8c4fff15a..43bd68793 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -182,6 +182,9 @@ struct Type
int32 maplineno; // first use of TFORW as map key
int32 embedlineno; // first use of TFORW as embedded type
+
+ // for TFORW, where to copy the eventual value to
+ NodeList *copyto;
};
#define T ((Type*)0)
@@ -940,7 +943,6 @@ Node* methodname(Node *n, Type *t);
Node* methodname1(Node *n, Node *t);
Sym* methodsym(Sym *nsym, Type *t0, int iface);
Node* newname(Sym *s);
-Type* newtype(Sym *s);
Node* oldname(Sym *s);
void popdcl(void);
void poptodcl(void);
@@ -1248,9 +1250,7 @@ int islvalue(Node *n);
Node* typecheck(Node **np, int top);
void typechecklist(NodeList *l, int top);
Node* typecheckdef(Node *n);
-void resumetypecopy(void);
void copytype(Node *n, Type *t);
-void defertypecopy(Node *n, Type *t);
void queuemethod(Node *n);
/*
diff --git a/src/cmd/gc/inl.c b/src/cmd/gc/inl.c
index efce56057..b2b1faff7 100644
--- a/src/cmd/gc/inl.c
+++ b/src/cmd/gc/inl.c
@@ -506,6 +506,19 @@ mkinlcall(Node **np, Node *fn)
mkinlcall1(np, fn);
safemode = save_safemode;
}
+
+static Node*
+tinlvar(Type *t)
+{
+ if(t->nname && !isblank(t->nname)) {
+ if(!t->nname->inlvar)
+ fatal("missing inlvar for %N\n", t->nname);
+ return t->nname->inlvar;
+ }
+ typecheck(&nblank, Erv | Easgn);
+ return nblank;
+}
+
// if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL.
// On return ninit has the parameter assignments, the nbody is the
// inlined function body and list, rlist contain the input, output
@@ -579,15 +592,12 @@ mkinlcall1(Node **np, Node *fn)
fatal("method call without receiver: %+N", n);
if(t == T)
fatal("method call unknown receiver type: %+N", n);
- if(t->nname != N && !isblank(t->nname))
- as = nod(OAS, t->nname->inlvar, n->left->left);
- else
- as = nod(OAS, temp(t->type), n->left->left);
+ as = nod(OAS, tinlvar(t), n->left->left);
} else { // non-method call to method
- if (!n->list)
+ if(!n->list)
fatal("non-method call to method without first arg: %+N", n);
- if(t != T && t->nname != N && !isblank(t->nname))
- as = nod(OAS, t->nname->inlvar, n->list->n);
+ if(t != T)
+ as = nod(OAS, tinlvar(t), n->list->n);
}
if(as != N) {
@@ -601,27 +611,16 @@ mkinlcall1(Node **np, Node *fn)
// TODO check that n->list->n is a call?
// TODO: non-method call to T.meth(f()) where f returns t, args...
as->rlist = n->list;
- for(t = getinargx(fn->type)->type; t; t=t->down) {
- if(t->nname && !isblank(t->nname)) {
- if(!t->nname->inlvar)
- fatal("missing inlvar for %N\n", t->nname);
- as->list = list(as->list, t->nname->inlvar);
- } else {
- as->list = list(as->list, temp(t->type));
- }
- }
+ for(t = getinargx(fn->type)->type; t; t=t->down)
+ as->list = list(as->list, tinlvar(t));
} else {
ll = n->list;
if(fn->type->thistuple && n->left->op != ODOTMETH) // non method call to method
ll=ll->next; // was handled above in if(thistuple)
for(t = getinargx(fn->type)->type; t && ll; t=t->down) {
- if(t->nname && !isblank(t->nname)) {
- if(!t->nname->inlvar)
- fatal("missing inlvar for %N\n", t->nname);
- as->list = list(as->list, t->nname->inlvar);
- as->rlist = list(as->rlist, ll->n);
- }
+ as->list = list(as->list, tinlvar(t));
+ as->rlist = list(as->rlist, ll->n);
ll=ll->next;
}
if(ll || t)
diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c
index e71fd3848..624dfb0b4 100644
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -348,7 +348,6 @@ main(int argc, char *argv[])
for(l=xtop; l; l=l->next)
if(l->n->op == ODCL || l->n->op == OAS)
typecheck(&l->n, Etop);
- resumetypecopy();
resumecheckwidth();
// Phase 3: Type check function bodies.
diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c
index 9bcd833a7..d301e4e4f 100644
--- a/src/cmd/gc/range.c
+++ b/src/cmd/gc/range.c
@@ -152,9 +152,14 @@ walkrange(Node *n)
n->ntest = nod(OLT, hv1, hn);
n->nincr = nod(OASOP, hv1, nodintconst(1));
n->nincr->etype = OADD;
- body = list1(nod(OAS, v1, hv1));
- if(v2) {
- body = list(body, nod(OAS, v2, nod(OIND, hp, N)));
+ if(v2 == N)
+ body = list1(nod(OAS, v1, hv1));
+ else {
+ a = nod(OAS2, N, N);
+ a->list = list(list1(v1), v2);
+ a->rlist = list(list1(hv1), nod(OIND, hp, N));
+ body = list1(a);
+
tmp = nod(OADD, hp, nodintconst(t->type->width));
tmp->type = hp->type;
tmp->typecheck = 1;
diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c
index 07b426508..62759bcba 100644
--- a/src/cmd/gc/reflect.c
+++ b/src/cmd/gc/reflect.c
@@ -629,6 +629,7 @@ typename(Type *t)
n->ullman = 1;
n->class = PEXTERN;
n->xoffset = 0;
+ n->typecheck = 1;
s->def = n;
signatlist = list(signatlist, typenod(t));
@@ -638,6 +639,7 @@ typename(Type *t)
n->type = ptrto(s->def->type);
n->addable = 1;
n->ullman = 2;
+ n->typecheck = 1;
return n;
}
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index 681c023a0..bd53520df 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -1945,6 +1945,12 @@ safeexpr(Node *n, NodeList **init)
if(n == N)
return N;
+ if(n->ninit) {
+ walkstmtlist(n->ninit);
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ }
+
switch(n->op) {
case ONAME:
case OLITERAL:
@@ -2673,7 +2679,7 @@ genhash(Sym *sym, Type *t)
first = T;
for(t1=t->type;; t1=t1->down) {
if(t1 != T && (isblanksym(t1->sym) || algtype1(t1->type, nil) == AMEM)) {
- if(first == T)
+ if(first == T && !isblanksym(t1->sym))
first = t1;
continue;
}
@@ -2890,7 +2896,7 @@ geneq(Sym *sym, Type *t)
first = T;
for(t1=t->type;; t1=t1->down) {
if(t1 != T && (isblanksym(t1->sym) || algtype1(t1->type, nil) == AMEM)) {
- if(first == T)
+ if(first == T && !isblanksym(t1->sym))
first = t1;
continue;
}
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index e98d53857..cc4faf5a7 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -31,7 +31,6 @@ static void checkassign(Node*);
static void checkassignlist(NodeList*);
static void stringtoarraylit(Node**);
static Node* resolve(Node*);
-static Type* getforwtype(Node*);
static NodeList* typecheckdefstack;
@@ -237,7 +236,7 @@ typecheck1(Node **np, int top)
Node *n, *l, *r;
NodeList *args;
int ok, ntop;
- Type *t, *tp, *ft, *missing, *have, *badtype;
+ Type *t, *tp, *missing, *have, *badtype;
Val v;
char *why;
@@ -249,10 +248,6 @@ typecheck1(Node **np, int top)
goto error;
}
- // a dance to handle forward-declared recursive pointer types.
- if(n->op == OTYPE && (ft = getforwtype(n->ntype)) != T)
- defertypecopy(n, ft);
-
typecheckdef(n);
n->realtype = n->type;
if(n->op == ONONAME)
@@ -1140,6 +1135,10 @@ reswitch:
goto error;
n->type = t;
if(!isslice(t)) {
+ if(isconst(args->n, CTNIL)) {
+ yyerror("first argument to append must be typed slice; have untyped nil", t);
+ goto error;
+ }
yyerror("first argument to append must be slice; have %lT", t);
goto error;
}
@@ -2612,26 +2611,6 @@ stringtoarraylit(Node **np)
*np = nn;
}
-static Type*
-getforwtype(Node *n)
-{
- Node *f1, *f2;
-
- for(f2=n; ; n=n->ntype) {
- if((n = resolve(n)) == N || n->op != OTYPE)
- return T;
-
- if(n->type != T && n->type->etype == TFORW)
- return n->type;
-
- // Check for ntype cycle.
- if((f2 = resolve(f2)) != N && (f1 = resolve(f2->ntype)) != N) {
- f2 = resolve(f1->ntype);
- if(f1 == n || f2 == n)
- return T;
- }
- }
-}
static int ntypecheckdeftype;
static NodeList *methodqueue;
@@ -2669,49 +2648,24 @@ domethod(Node *n)
checkwidth(n->type);
}
-typedef struct NodeTypeList NodeTypeList;
-struct NodeTypeList {
- Node *n;
- Type *t;
- NodeTypeList *next;
-};
-
-static NodeTypeList *dntq;
-static NodeTypeList *dntend;
+static NodeList *mapqueue;
void
-defertypecopy(Node *n, Type *t)
+copytype(Node *n, Type *t)
{
- NodeTypeList *ntl;
+ int maplineno, embedlineno, lno;
+ NodeList *l;
- if(n == N || t == T)
+ if(t->etype == TFORW) {
+ // This type isn't computed yet; when it is, update n.
+ t->copyto = list(t->copyto, n);
return;
+ }
- ntl = mal(sizeof *ntl);
- ntl->n = n;
- ntl->t = t;
- ntl->next = nil;
-
- if(dntq == nil)
- dntq = ntl;
- else
- dntend->next = ntl;
-
- dntend = ntl;
-}
-
-void
-resumetypecopy(void)
-{
- NodeTypeList *l;
-
- for(l=dntq; l; l=l->next)
- copytype(l->n, l->t);
-}
+ maplineno = n->type->maplineno;
+ embedlineno = n->type->embedlineno;
-void
-copytype(Node *n, Type *t)
-{
+ l = n->type->copyto;
*n->type = *t;
t = n->type;
@@ -2724,12 +2678,32 @@ copytype(Node *n, Type *t)
t->nod = N;
t->printed = 0;
t->deferwidth = 0;
+ t->copyto = nil;
+
+ // Update nodes waiting on this type.
+ for(; l; l=l->next)
+ copytype(l->n, t);
+
+ // Double-check use of type as embedded type.
+ lno = lineno;
+ if(embedlineno) {
+ lineno = embedlineno;
+ if(isptr[t->etype])
+ yyerror("embedded type cannot be a pointer");
+ }
+ lineno = lno;
+
+ // Queue check for map until all the types are done settling.
+ if(maplineno) {
+ t->maplineno = maplineno;
+ mapqueue = list(mapqueue, n);
+ }
}
static void
typecheckdeftype(Node *n)
{
- int maplineno, embedlineno, lno;
+ int lno;
Type *t;
NodeList *l;
@@ -2748,26 +2722,12 @@ typecheckdeftype(Node *n)
goto ret;
}
- maplineno = n->type->maplineno;
- embedlineno = n->type->embedlineno;
-
// copy new type and clear fields
// that don't come along.
// anything zeroed here must be zeroed in
// typedcl2 too.
copytype(n, t);
- // double-check use of type as map key.
- if(maplineno) {
- lineno = maplineno;
- maptype(n->type, types[TBOOL]);
- }
- if(embedlineno) {
- lineno = embedlineno;
- if(isptr[t->etype])
- yyerror("embedded type cannot be a pointer");
- }
-
ret:
lineno = lno;
@@ -2780,6 +2740,11 @@ ret:
for(; l; l=l->next)
domethod(l->n);
}
+ for(l=mapqueue; l; l=l->next) {
+ lineno = l->n->type->maplineno;
+ maptype(l->n->type, types[TBOOL]);
+ }
+ lineno = lno;
}
ntypecheckdeftype--;
}
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
index 7dfd34a7a..a4edc9062 100644
--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -846,11 +846,13 @@ walkexpr(Node **np, NodeList **init)
// if range of type cannot exceed static array bound,
// disable bounds check
if(isfixedarray(n->left->type))
+ if(!issigned[n->right->type->etype])
if(n->right->type->width < 4)
if((1<<(8*n->right->type->width)) <= n->left->type->bound)
n->etype = 1;
if(isconst(n->left, CTSTR))
+ if(!issigned[n->right->type->etype])
if(n->right->type->width < 4)
if((1<<(8*n->right->type->width)) <= n->left->val.u.sval->len)
n->etype = 1;
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index 4bb83f161..767ddfd40 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -152,6 +152,11 @@ func addBuildFlags(cmd *Command) {
cmd.Flag.Var(buildCompiler{}, "compiler", "")
}
+func addBuildFlagsNX(cmd *Command) {
+ cmd.Flag.BoolVar(&buildN, "n", false, "")
+ cmd.Flag.BoolVar(&buildX, "x", false, "")
+}
+
type stringsFlag []string
func (v *stringsFlag) Set(s string) error {
diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go
index 32ede3964..5e7b10692 100644
--- a/src/cmd/go/doc.go
+++ b/src/cmd/go/doc.go
@@ -145,7 +145,7 @@ Run godoc on package sources
Usage:
- go doc [packages]
+ go doc [-n] [-x] [packages]
Doc runs the godoc command on the packages named by the
import paths.
@@ -153,6 +153,9 @@ import paths.
For more about godoc, see 'godoc godoc'.
For more about specifying packages, see 'go help packages'.
+The -n flag prints commands that would be executed.
+The -x flag prints commands as they are executed.
+
To run godoc with specific options, run godoc itself.
See also: go fix, go fmt, go vet.
@@ -192,7 +195,7 @@ Run gofmt on package sources
Usage:
- go fmt [packages]
+ go fmt [-n] [-x] [packages]
Fmt runs the command 'gofmt -l -w' on the packages named
by the import paths. It prints the names of the files that are modified.
@@ -200,6 +203,9 @@ by the import paths. It prints the names of the files that are modified.
For more about gofmt, see 'godoc gofmt'.
For more about specifying packages, see 'go help packages'.
+The -n flag prints commands that would be executed.
+The -x flag prints commands as they are executed.
+
To run gofmt with specific options, run gofmt itself.
See also: go doc, go fix, go vet.
@@ -414,7 +420,7 @@ Run go tool vet on packages
Usage:
- go vet [packages]
+ go vet [-n] [-x] [packages]
Vet runs the Go vet command on the packages named by the import paths.
@@ -423,6 +429,9 @@ For more about specifying packages, see 'go help packages'.
To run the vet tool with specific options, run 'go tool vet'.
+The -n flag prints commands that would be executed.
+The -x flag prints commands as they are executed.
+
See also: go fmt, go fix.
@@ -537,7 +546,7 @@ in those files and ignoring any other files in the directory.
Remote import path syntax
-An import path (see 'go help importpath') denotes a package
+An import path (see 'go help packages') denotes a package
stored in the local file system. Certain import paths also
describe how to obtain the source code for the package using
a revision control system.
diff --git a/src/cmd/go/fmt.go b/src/cmd/go/fmt.go
index cea9b0a51..b1aba32f3 100644
--- a/src/cmd/go/fmt.go
+++ b/src/cmd/go/fmt.go
@@ -4,9 +4,14 @@
package main
+func init() {
+ addBuildFlagsNX(cmdFmt)
+ addBuildFlagsNX(cmdDoc)
+}
+
var cmdFmt = &Command{
Run: runFmt,
- UsageLine: "fmt [packages]",
+ UsageLine: "fmt [-n] [-x] [packages]",
Short: "run gofmt on package sources",
Long: `
Fmt runs the command 'gofmt -l -w' on the packages named
@@ -15,6 +20,9 @@ by the import paths. It prints the names of the files that are modified.
For more about gofmt, see 'godoc gofmt'.
For more about specifying packages, see 'go help packages'.
+The -n flag prints commands that would be executed.
+The -x flag prints commands as they are executed.
+
To run gofmt with specific options, run gofmt itself.
See also: go doc, go fix, go vet.
@@ -32,7 +40,7 @@ func runFmt(cmd *Command, args []string) {
var cmdDoc = &Command{
Run: runDoc,
- UsageLine: "doc [packages]",
+ UsageLine: "doc [-n] [-x] [packages]",
Short: "run godoc on package sources",
Long: `
Doc runs the godoc command on the packages named by the
@@ -41,6 +49,9 @@ import paths.
For more about godoc, see 'godoc godoc'.
For more about specifying packages, see 'go help packages'.
+The -n flag prints commands that would be executed.
+The -x flag prints commands as they are executed.
+
To run godoc with specific options, run godoc itself.
See also: go fix, go fmt, go vet.
@@ -53,6 +64,10 @@ func runDoc(cmd *Command, args []string) {
errorf("go doc: cannot use package file list")
continue
}
- run("godoc", pkg.Dir)
+ if pkg.local {
+ run("godoc", pkg.Dir)
+ } else {
+ run("godoc", pkg.ImportPath)
+ }
}
}
diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go
index 47ea0c711..7539753af 100644
--- a/src/cmd/go/help.go
+++ b/src/cmd/go/help.go
@@ -61,7 +61,7 @@ var helpRemote = &Command{
Short: "remote import path syntax",
Long: `
-An import path (see 'go help importpath') denotes a package
+An import path (see 'go help packages') denotes a package
stored in the local file system. Certain import paths also
describe how to obtain the source code for the package using
a revision control system.
@@ -138,7 +138,7 @@ The meta tag has the form:
<meta name="go-import" content="import-prefix vcs repo-root">
-The import-prefix is the import path correponding to the repository
+The import-prefix is the import path corresponding to the repository
root. It must be a prefix or an exact match of the package being
fetched with "go get". If it's not an exact match, another http
request is made at the prefix to verify the <meta> tags match.
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index 73c2f54a7..20585d1be 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -144,8 +144,9 @@ func main() {
}
}
- fmt.Fprintf(os.Stderr, "Unknown command %#q\n\n", args[0])
- usage()
+ fmt.Fprintf(os.Stderr, "go: unknown subcommand %q\nRun 'go help' for usage.\n", args[0])
+ setExitStatus(2)
+ exit()
}
var usageTemplate = `Go is a tool for managing Go source code.
@@ -339,6 +340,13 @@ func exitIfErrors() {
func run(cmdargs ...interface{}) {
cmdline := stringList(cmdargs...)
+ if buildN || buildV {
+ fmt.Printf("%s\n", strings.Join(cmdline, " "))
+ if buildN {
+ return
+ }
+ }
+
cmd := exec.Command(cmdline[0], cmdline[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -500,13 +508,25 @@ func matchPackagesInFS(pattern string) []string {
var pkgs []string
filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
- if err != nil || !fi.IsDir() || path == dir {
+ if err != nil || !fi.IsDir() {
return nil
}
+ if path == dir {
+ // filepath.Walk starts at dir and recurses. For the recursive case,
+ // the path is the result of filepath.Join, which calls filepath.Clean.
+ // The initial case is not Cleaned, though, so we do this explicitly.
+ //
+ // This converts a path like "./io/" to "io". Without this step, running
+ // "cd $GOROOT/src/pkg; go list ./io/..." would incorrectly skip the io
+ // package, because prepending the prefix "./" to the unclean path would
+ // result in "././io", and match("././io") returns false.
+ path = filepath.Clean(path)
+ }
- // Avoid .foo, _foo, and testdata directory trees.
+ // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
_, elem := filepath.Split(path)
- if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
+ dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
+ if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
return filepath.SkipDir
}
diff --git a/src/cmd/go/run.go b/src/cmd/go/run.go
index 94cd59296..6043b7e20 100644
--- a/src/cmd/go/run.go
+++ b/src/cmd/go/run.go
@@ -49,6 +49,10 @@ func runRun(cmd *Command, args []string) {
if p.Error != nil {
fatalf("%s", p.Error)
}
+ for _, err := range p.DepsErrors {
+ errorf("%s", err)
+ }
+ exitIfErrors()
if p.Name != "main" {
fatalf("go run: cannot run non-main package")
}
diff --git a/src/cmd/go/vet.go b/src/cmd/go/vet.go
index a672b9910..eb0b89cca 100644
--- a/src/cmd/go/vet.go
+++ b/src/cmd/go/vet.go
@@ -4,9 +4,13 @@
package main
+func init() {
+ addBuildFlagsNX(cmdVet)
+}
+
var cmdVet = &Command{
Run: runVet,
- UsageLine: "vet [packages]",
+ UsageLine: "vet [-n] [-x] [packages]",
Short: "run go tool vet on packages",
Long: `
Vet runs the Go vet command on the packages named by the import paths.
@@ -16,6 +20,9 @@ For more about specifying packages, see 'go help packages'.
To run the vet tool with specific options, run 'go tool vet'.
+The -n flag prints commands that would be executed.
+The -x flag prints commands as they are executed.
+
See also: go fmt, go fix.
`,
}
diff --git a/src/lib9/ctime.c b/src/lib9/ctime.c
index 1bc29fbf9..6317b594b 100644
--- a/src/lib9/ctime.c
+++ b/src/lib9/ctime.c
@@ -16,8 +16,8 @@ p9ctime(long t)
tt = t;
tm = localtime(&tt);
snprint(buf, sizeof buf, "%3.3s %3.3s %02d %02d:%02d:%02d %3.3s %d\n",
- "SunMonTueWedThuFriSat"+(tm->tm_wday*3),
- "JanFebMarAprMayJunJulAugSepOctNovDec"+(tm->tm_mon*3),
+ &"SunMonTueWedThuFriSat"[tm->tm_wday*3],
+ &"JanFebMarAprMayJunJulAugSepOctNovDec"[tm->tm_mon*3],
tm->tm_mday,
tm->tm_hour,
tm->tm_min,
diff --git a/src/pkg/compress/flate/deflate.go b/src/pkg/compress/flate/deflate.go
index 20408409c..e511b50fd 100644
--- a/src/pkg/compress/flate/deflate.go
+++ b/src/pkg/compress/flate/deflate.go
@@ -32,6 +32,7 @@ const (
hashSize = 1 << hashBits
hashMask = (1 << hashBits) - 1
hashShift = (hashBits + minMatchLength - 1) / minMatchLength
+ maxHashOffset = 1 << 24
skipNever = math.MaxInt32
)
@@ -106,6 +107,25 @@ func (d *compressor) fillDeflate(b []byte) int {
d.blockStart = math.MaxInt32
}
d.hashOffset += windowSize
+ if d.hashOffset > maxHashOffset {
+ delta := d.hashOffset - 1
+ d.hashOffset -= delta
+ d.chainHead -= delta
+ for i, v := range d.hashPrev {
+ if v > delta {
+ d.hashPrev[i] -= delta
+ } else {
+ d.hashPrev[i] = 0
+ }
+ }
+ for i, v := range d.hashHead {
+ if v > delta {
+ d.hashHead[i] -= delta
+ } else {
+ d.hashHead[i] = 0
+ }
+ }
+ }
}
n := copy(d.window[d.windowEnd:], b)
d.windowEnd += n
diff --git a/src/pkg/compress/flate/deflate_test.go b/src/pkg/compress/flate/deflate_test.go
index 543c59505..f1e6db2ac 100644
--- a/src/pkg/compress/flate/deflate_test.go
+++ b/src/pkg/compress/flate/deflate_test.go
@@ -94,6 +94,50 @@ func TestDeflate(t *testing.T) {
}
}
+// A sparseReader returns a stream consisting of 0s followed by 1<<16 1s.
+// This tests missing hash references in a very large input.
+type sparseReader struct {
+ l int64
+ cur int64
+}
+
+func (r *sparseReader) Read(b []byte) (n int, err error) {
+ if r.cur >= r.l {
+ return 0, io.EOF
+ }
+ n = len(b)
+ cur := r.cur + int64(n)
+ if cur > r.l {
+ n -= int(cur - r.l)
+ cur = r.l
+ }
+ for i := range b[0:n] {
+ if r.cur+int64(i) >= r.l-1<<16 {
+ b[i] = 1
+ } else {
+ b[i] = 0
+ }
+ }
+ r.cur = cur
+ return
+}
+
+func TestVeryLongSparseChunk(t *testing.T) {
+ if testing.Short() {
+ t.Logf("skipping sparse chunk during short test")
+ return
+ }
+ w, err := NewWriter(ioutil.Discard, 1)
+ if err != nil {
+ t.Errorf("NewWriter: %v", err)
+ return
+ }
+ if _, err = io.Copy(w, &sparseReader{l: 23E8}); err != nil {
+ t.Errorf("Compress failed: %v", err)
+ return
+ }
+}
+
type syncBuffer struct {
buf bytes.Buffer
mu sync.RWMutex
diff --git a/src/pkg/crypto/aes/const.go b/src/pkg/crypto/aes/const.go
index f0b4eabf6..aee73a7c5 100644
--- a/src/pkg/crypto/aes/const.go
+++ b/src/pkg/crypto/aes/const.go
@@ -11,11 +11,11 @@ package aes
// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf
// AES is based on the mathematical behavior of binary polynomials
-// (polynomials over GF(2)) modulo the irreducible polynomial x⁸ + x⁴ + x² + x + 1.
+// (polynomials over GF(2)) modulo the irreducible polynomial x⁸ + x⁴ + x³ + x + 1.
// Addition of these binary polynomials corresponds to binary xor.
// Reducing mod poly corresponds to binary xor with poly every
// time a 0x100 bit appears.
-const poly = 1<<8 | 1<<4 | 1<<3 | 1<<1 | 1<<0 // x⁸ + x⁴ + x² + x + 1
+const poly = 1<<8 | 1<<4 | 1<<3 | 1<<1 | 1<<0 // x⁸ + x⁴ + x³ + x + 1
// Powers of x mod poly in GF(2).
var powx = [16]byte{
diff --git a/src/pkg/crypto/ecdsa/ecdsa.go b/src/pkg/crypto/ecdsa/ecdsa.go
index b28239b78..8508e3b4f 100644
--- a/src/pkg/crypto/ecdsa/ecdsa.go
+++ b/src/pkg/crypto/ecdsa/ecdsa.go
@@ -66,7 +66,9 @@ func GenerateKey(c elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error)
// hashToInt converts a hash value to an integer. There is some disagreement
// about how this is done. [NSA] suggests that this is done in the obvious
// manner, but [SECG] truncates the hash to the bit-length of the curve order
-// first. We follow [SECG] because that's what OpenSSL does.
+// first. We follow [SECG] because that's what OpenSSL does. Additionally,
+// OpenSSL right shifts excess bits from the number if the hash is too large
+// and we mirror that too.
func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
orderBits := c.Params().N.BitLen()
orderBytes := (orderBits + 7) / 8
@@ -75,7 +77,7 @@ func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
}
ret := new(big.Int).SetBytes(hash)
- excess := orderBytes*8 - orderBits
+ excess := len(hash)*8 - orderBits
if excess > 0 {
ret.Rsh(ret, uint(excess))
}
diff --git a/src/pkg/crypto/rsa/pkcs1v15.go b/src/pkg/crypto/rsa/pkcs1v15.go
index 254f4a3da..a32236e47 100644
--- a/src/pkg/crypto/rsa/pkcs1v15.go
+++ b/src/pkg/crypto/rsa/pkcs1v15.go
@@ -151,6 +151,7 @@ func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) {
var hashPrefixes = map[crypto.Hash][]byte{
crypto.MD5: {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
crypto.SHA1: {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
+ crypto.SHA224: {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c},
crypto.SHA256: {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
crypto.SHA384: {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
crypto.SHA512: {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
diff --git a/src/pkg/crypto/tls/handshake_messages.go b/src/pkg/crypto/tls/handshake_messages.go
index e1517cc79..54c7a3e63 100644
--- a/src/pkg/crypto/tls/handshake_messages.go
+++ b/src/pkg/crypto/tls/handshake_messages.go
@@ -563,7 +563,7 @@ func (m *certificateMsg) unmarshal(data []byte) bool {
if len(d) < 4 {
return false
}
- certLen := uint32(d[0])<<24 | uint32(d[1])<<8 | uint32(d[2])
+ certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
if uint32(len(d)) < 3+certLen {
return false
}
@@ -575,7 +575,7 @@ func (m *certificateMsg) unmarshal(data []byte) bool {
m.certificates = make([][]byte, numCerts)
d = data[7:]
for i := 0; i < numCerts; i++ {
- certLen := uint32(d[0])<<24 | uint32(d[1])<<8 | uint32(d[2])
+ certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
m.certificates[i] = d[3 : 3+certLen]
d = d[3+certLen:]
}
diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go
index 8dae7e7fc..c4d85e67f 100644
--- a/src/pkg/crypto/x509/x509.go
+++ b/src/pkg/crypto/x509/x509.go
@@ -388,10 +388,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
return ErrUnsupportedAlgorithm
}
- h := hashType.New()
- if h == nil {
+ if !hashType.Available() {
return ErrUnsupportedAlgorithm
}
+ h := hashType.New()
h.Write(signed)
digest := h.Sum(nil)
diff --git a/src/pkg/debug/gosym/pclntab_test.go b/src/pkg/debug/gosym/pclntab_test.go
index b2400bb3b..ade704335 100644
--- a/src/pkg/debug/gosym/pclntab_test.go
+++ b/src/pkg/debug/gosym/pclntab_test.go
@@ -7,14 +7,19 @@ package gosym
import (
"debug/elf"
"fmt"
+ "io/ioutil"
"os"
"os/exec"
+ "path/filepath"
"runtime"
"strings"
"testing"
)
-var pclinetestBinary string
+var (
+ pclineTempDir string
+ pclinetestBinary string
+)
func dotest() bool {
// For now, only works on ELF platforms.
@@ -24,10 +29,18 @@ func dotest() bool {
if pclinetestBinary != "" {
return true
}
+ var err error
+ pclineTempDir, err = ioutil.TempDir("", "pclinetest")
+ if err != nil {
+ panic(err)
+ }
+ if strings.Contains(pclineTempDir, " ") {
+ panic("unexpected space in tempdir")
+ }
// This command builds pclinetest from pclinetest.asm;
// the resulting binary looks like it was built from pclinetest.s,
// but we have renamed it to keep it away from the go tool.
- pclinetestBinary = os.TempDir() + "/pclinetest"
+ pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -E main -o %s %s.6",
pclinetestBinary, pclinetestBinary, pclinetestBinary)
cmd := exec.Command("sh", "-c", command)
@@ -170,6 +183,7 @@ func TestPCLine(t *testing.T) {
if !dotest() {
return
}
+ defer os.RemoveAll(pclineTempDir)
f, tab := crack(pclinetestBinary, t)
text := f.Section(".text")
diff --git a/src/pkg/encoding/base64/base64.go b/src/pkg/encoding/base64/base64.go
index f8a51a4e7..0b842f066 100644
--- a/src/pkg/encoding/base64/base64.go
+++ b/src/pkg/encoding/base64/base64.go
@@ -318,7 +318,7 @@ func (d *decoder) Read(p []byte) (n int, err error) {
}
nn, d.err = io.ReadAtLeast(d.r, d.buf[d.nbuf:nn], 4-d.nbuf)
d.nbuf += nn
- if d.nbuf < 4 {
+ if d.err != nil || d.nbuf < 4 {
return 0, d.err
}
diff --git a/src/pkg/encoding/base64/base64_test.go b/src/pkg/encoding/base64/base64_test.go
index 9c3537259..f9b863c36 100644
--- a/src/pkg/encoding/base64/base64_test.go
+++ b/src/pkg/encoding/base64/base64_test.go
@@ -6,9 +6,11 @@ package base64
import (
"bytes"
+ "errors"
"io"
"io/ioutil"
"testing"
+ "time"
)
type testpair struct {
@@ -226,3 +228,50 @@ func TestNewLineCharacters(t *testing.T) {
}
}
}
+
+type nextRead struct {
+ n int // bytes to return
+ err error // error to return
+}
+
+// faultInjectReader returns data from source, rate-limited
+// and with the errors as written to nextc.
+type faultInjectReader struct {
+ source string
+ nextc <-chan nextRead
+}
+
+func (r *faultInjectReader) Read(p []byte) (int, error) {
+ nr := <-r.nextc
+ if len(p) > nr.n {
+ p = p[:nr.n]
+ }
+ n := copy(p, r.source)
+ r.source = r.source[n:]
+ return n, nr.err
+}
+
+// tests that we don't ignore errors from our underlying reader
+func TestDecoderIssue3577(t *testing.T) {
+ next := make(chan nextRead, 10)
+ wantErr := errors.New("my error")
+ next <- nextRead{5, nil}
+ next <- nextRead{10, wantErr}
+ d := NewDecoder(StdEncoding, &faultInjectReader{
+ source: "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==", // twas brillig...
+ nextc: next,
+ })
+ errc := make(chan error)
+ go func() {
+ _, err := ioutil.ReadAll(d)
+ errc <- err
+ }()
+ select {
+ case err := <-errc:
+ if err != wantErr {
+ t.Errorf("got error %v; want %v", err, wantErr)
+ }
+ case <-time.After(5 * time.Second):
+ t.Errorf("timeout; Decoder blocked without returning an error")
+ }
+}
diff --git a/src/pkg/encoding/gob/doc.go b/src/pkg/encoding/gob/doc.go
index 96885f8de..821d9a3fe 100644
--- a/src/pkg/encoding/gob/doc.go
+++ b/src/pkg/encoding/gob/doc.go
@@ -116,7 +116,7 @@ uninterpreted bytes of the value.
All other slices and arrays are sent as an unsigned count followed by that many
elements using the standard gob encoding for their type, recursively.
-Maps are sent as an unsigned count followed by that man key, element
+Maps are sent as an unsigned count followed by that many key, element
pairs. Empty but non-nil maps are sent, so if the sender has allocated
a map, the receiver will allocate a map even no elements are
transmitted.
diff --git a/src/pkg/encoding/json/decode.go b/src/pkg/encoding/json/decode.go
index 110c6fd62..d61f88706 100644
--- a/src/pkg/encoding/json/decode.go
+++ b/src/pkg/encoding/json/decode.go
@@ -273,9 +273,14 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler,
_, isUnmarshaler = v.Interface().(Unmarshaler)
}
+ // Load value from interface, but only if the result will be
+ // usefully addressable.
if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() {
- v = iv.Elem()
- continue
+ e := iv.Elem()
+ if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
+ v = e
+ continue
+ }
}
pv := v
@@ -588,6 +593,11 @@ func (d *decodeState) literal(v reflect.Value) {
// produce more helpful error messages.
func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) {
// Check for unmarshaler.
+ if len(item) == 0 {
+ //Empty string given
+ d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
+ return
+ }
wantptr := item[0] == 'n' // null
unmarshaler, pv := d.indirect(v, wantptr)
if unmarshaler != nil {
diff --git a/src/pkg/encoding/json/decode_test.go b/src/pkg/encoding/json/decode_test.go
index d758758d9..6fac22c4a 100644
--- a/src/pkg/encoding/json/decode_test.go
+++ b/src/pkg/encoding/json/decode_test.go
@@ -638,3 +638,68 @@ func TestAnonymous(t *testing.T) {
t.Fatal("Unmarshal: did set T.Y")
}
}
+
+// Test that the empty string doesn't panic decoding when ,string is specified
+// Issue 3450
+func TestEmptyString(t *testing.T) {
+ type T2 struct {
+ Number1 int `json:",string"`
+ Number2 int `json:",string"`
+ }
+ data := `{"Number1":"1", "Number2":""}`
+ dec := NewDecoder(strings.NewReader(data))
+ var t2 T2
+ err := dec.Decode(&t2)
+ if err == nil {
+ t.Fatal("Decode: did not return error")
+ }
+ if t2.Number1 != 1 {
+ t.Fatal("Decode: did not set Number1")
+ }
+}
+
+func intp(x int) *int {
+ p := new(int)
+ *p = x
+ return p
+}
+
+func intpp(x *int) **int {
+ pp := new(*int)
+ *pp = x
+ return pp
+}
+
+var interfaceSetTests = []struct {
+ pre interface{}
+ json string
+ post interface{}
+}{
+ {"foo", `"bar"`, "bar"},
+ {"foo", `2`, 2.0},
+ {"foo", `true`, true},
+ {"foo", `null`, nil},
+
+ {nil, `null`, nil},
+ {new(int), `null`, nil},
+ {(*int)(nil), `null`, nil},
+ {new(*int), `null`, new(*int)},
+ {(**int)(nil), `null`, nil},
+ {intp(1), `null`, nil},
+ {intpp(nil), `null`, intpp(nil)},
+ {intpp(intp(1)), `null`, intpp(nil)},
+}
+
+func TestInterfaceSet(t *testing.T) {
+ for _, tt := range interfaceSetTests {
+ b := struct{ X interface{} }{tt.pre}
+ blob := `{"X":` + tt.json + `}`
+ if err := Unmarshal([]byte(blob), &b); err != nil {
+ t.Errorf("Unmarshal %#q: %v", blob, err)
+ continue
+ }
+ if !reflect.DeepEqual(b.X, tt.post) {
+ t.Errorf("Unmarshal %#q into %#v: X=%#v, want %#v", blob, tt.pre, b.X, tt.post)
+ }
+ }
+}
diff --git a/src/pkg/encoding/json/encode.go b/src/pkg/encoding/json/encode.go
index 842672c39..b6e1cb16e 100644
--- a/src/pkg/encoding/json/encode.go
+++ b/src/pkg/encoding/json/encode.go
@@ -96,7 +96,7 @@ import (
//
// Channel, complex, and function values cannot be encoded in JSON.
// Attempting to encode such a value causes Marshal to return
-// an InvalidTypeError.
+// an UnsupportedTypeError.
//
// JSON cannot represent cyclic data structures and Marshal does not
// handle them. Passing cyclic structures to Marshal will result in
@@ -157,6 +157,8 @@ type Marshaler interface {
MarshalJSON() ([]byte, error)
}
+// An UnsupportedTypeError is returned by Marshal when attempting
+// to encode an unsupported value type.
type UnsupportedTypeError struct {
Type reflect.Type
}
diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go
index f0842a18a..5444ad141 100644
--- a/src/pkg/flag/flag.go
+++ b/src/pkg/flag/flag.go
@@ -620,8 +620,9 @@ func (f *FlagSet) Var(value Value, name string, usage string) {
flag := &Flag{name, usage, value, value.String()}
_, alreadythere := f.formal[name]
if alreadythere {
- fmt.Fprintf(f.out(), "%s flag redefined: %s\n", f.name, name)
- panic("flag redefinition") // Happens only if flags are declared with identical names
+ msg := fmt.Sprintf("%s flag redefined: %s", f.name, name)
+ fmt.Fprintln(f.out(), msg)
+ panic(msg) // Happens only if flags are declared with identical names
}
if f.formal == nil {
f.formal = make(map[string]*Flag)
diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go
index 9660370c2..a9b9c9d0c 100644
--- a/src/pkg/fmt/doc.go
+++ b/src/pkg/fmt/doc.go
@@ -136,7 +136,7 @@
Fscanf and Fscanln read from a specified io.Reader; Sscan,
Sscanf and Sscanln read from an argument string. Scanln,
Fscanln and Sscanln stop scanning at a newline and require that
- the items be followed by one; Sscanf, Fscanf and Sscanf require
+ the items be followed by one; Scanf, Fscanf and Sscanf require
newlines in the input to match newlines in the format; the other
routines treat newlines as spaces.
diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go
index 7123fe58f..d2e75dc1c 100644
--- a/src/pkg/go/ast/ast.go
+++ b/src/pkg/go/ast/ast.go
@@ -87,8 +87,12 @@ func stripTrailingWhitespace(s string) string {
return s[0:i]
}
-// Text returns the text of the comment,
-// with the comment markers - //, /*, and */ - removed.
+// Text returns the text of the comment.
+// Comment markers (//, /*, and */), the first space of a line comment, and
+// leading and trailing empty lines are removed. Multiple empty lines are
+// reduced to one, and trailing space on lines is trimmed. Unless the result
+// is empty, it is newline-terminated.
+//
func (g *CommentGroup) Text() string {
if g == nil {
return ""
@@ -104,11 +108,9 @@ func (g *CommentGroup) Text() string {
// The parser has given us exactly the comment text.
switch c[1] {
case '/':
- //-style comment
+ //-style comment (no newline at the end)
c = c[2:]
- // Remove leading space after //, if there is one.
- // TODO(gri) This appears to be necessary in isolated
- // cases (bignum.RatFromString) - why?
+ // strip first space - required for Example tests
if len(c) > 0 && c[0] == ' ' {
c = c[1:]
}
diff --git a/src/pkg/go/ast/ast_test.go b/src/pkg/go/ast/ast_test.go
new file mode 100644
index 000000000..1a6a283f2
--- /dev/null
+++ b/src/pkg/go/ast/ast_test.go
@@ -0,0 +1,50 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ast
+
+import (
+ "testing"
+)
+
+var comments = []struct {
+ list []string
+ text string
+}{
+ {[]string{"//"}, ""},
+ {[]string{"// "}, ""},
+ {[]string{"//", "//", "// "}, ""},
+ {[]string{"// foo "}, "foo\n"},
+ {[]string{"//", "//", "// foo"}, "foo\n"},
+ {[]string{"// foo bar "}, "foo bar\n"},
+ {[]string{"// foo", "// bar"}, "foo\nbar\n"},
+ {[]string{"// foo", "//", "//", "//", "// bar"}, "foo\n\nbar\n"},
+ {[]string{"// foo", "/* bar */"}, "foo\n bar\n"},
+ {[]string{"//", "//", "//", "// foo", "//", "//", "//"}, "foo\n"},
+
+ {[]string{"/**/"}, ""},
+ {[]string{"/* */"}, ""},
+ {[]string{"/**/", "/**/", "/* */"}, ""},
+ {[]string{"/* Foo */"}, " Foo\n"},
+ {[]string{"/* Foo Bar */"}, " Foo Bar\n"},
+ {[]string{"/* Foo*/", "/* Bar*/"}, " Foo\n Bar\n"},
+ {[]string{"/* Foo*/", "/**/", "/**/", "/**/", "// Bar"}, " Foo\n\nBar\n"},
+ {[]string{"/* Foo*/", "/*\n*/", "//", "/*\n*/", "// Bar"}, " Foo\n\nBar\n"},
+ {[]string{"/* Foo*/", "// Bar"}, " Foo\nBar\n"},
+ {[]string{"/* Foo\n Bar*/"}, " Foo\n Bar\n"},
+}
+
+func TestCommentText(t *testing.T) {
+ for i, c := range comments {
+ list := make([]*Comment, len(c.list))
+ for i, s := range c.list {
+ list[i] = &Comment{Text: s}
+ }
+
+ text := (&CommentGroup{list}).Text()
+ if text != c.text {
+ t.Errorf("case %d: got %q; expected %q", i, text, c.text)
+ }
+ }
+}
diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go
index d749aef15..7a81d5030 100644
--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -68,7 +68,7 @@ type Context struct {
// ReadDir returns a slice of os.FileInfo, sorted by Name,
// describing the content of the named directory.
- // If ReadDir is nil, Import uses io.ReadDir.
+ // If ReadDir is nil, Import uses ioutil.ReadDir.
ReadDir func(dir string) (fi []os.FileInfo, err error)
// OpenFile opens a file (not a directory) for reading.
@@ -339,7 +339,7 @@ func (e *NoGoError) Error() string {
// - files starting with _ or . (likely editor temporary files)
// - files with build constraints not satisfied by the context
//
-// If an error occurs, Import returns a non-nil error also returns a non-nil
+// If an error occurs, Import returns a non-nil error and a non-nil
// *Package containing partial information.
//
func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) {
diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go
index e362e13a7..20e505d97 100644
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -267,13 +267,13 @@ func (p *parser) consumeComment() (comment *ast.Comment, endline int) {
// Consume a group of adjacent comments, add it to the parser's
// comments list, and return it together with the line at which
-// the last comment in the group ends. An empty line or non-comment
-// token terminates a comment group.
+// the last comment in the group ends. A non-comment token or n
+// empty lines terminate a comment group.
//
-func (p *parser) consumeCommentGroup() (comments *ast.CommentGroup, endline int) {
+func (p *parser) consumeCommentGroup(n int) (comments *ast.CommentGroup, endline int) {
var list []*ast.Comment
endline = p.file.Line(p.pos)
- for p.tok == token.COMMENT && endline+1 >= p.file.Line(p.pos) {
+ for p.tok == token.COMMENT && p.file.Line(p.pos) <= endline+n {
var comment *ast.Comment
comment, endline = p.consumeComment()
list = append(list, comment)
@@ -314,7 +314,7 @@ func (p *parser) next() {
if p.file.Line(p.pos) == line {
// The comment is on same line as the previous token; it
// cannot be a lead comment but may be a line comment.
- comment, endline = p.consumeCommentGroup()
+ comment, endline = p.consumeCommentGroup(0)
if p.file.Line(p.pos) != endline {
// The next token is on a different line, thus
// the last comment group is a line comment.
@@ -325,7 +325,7 @@ func (p *parser) next() {
// consume successor comments, if any
endline = -1
for p.tok == token.COMMENT {
- comment, endline = p.consumeCommentGroup()
+ comment, endline = p.consumeCommentGroup(1)
}
if endline+1 == p.file.Line(p.pos) {
@@ -627,10 +627,10 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
doc := p.leadComment
- // fields
+ // FieldDecl
list, typ := p.parseVarList(false)
- // optional tag
+ // Tag
var tag *ast.BasicLit
if p.tok == token.STRING {
tag = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
@@ -645,7 +645,6 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
} else {
// ["*"] TypeName (AnonymousField)
typ = list[0] // we always have at least one element
- p.resolve(typ)
if n := len(list); n > 1 || !isTypeName(deref(typ)) {
pos := typ.Pos()
p.errorExpected(pos, "anonymous field")
@@ -657,6 +656,7 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
field := &ast.Field{Doc: doc, Names: idents, Type: typ, Tag: tag, Comment: p.lineComment}
p.declare(field, nil, scope, ast.Var, idents...)
+ p.resolve(typ)
return field
}
@@ -699,12 +699,15 @@ func (p *parser) parsePointerType() *ast.StarExpr {
return &ast.StarExpr{Star: star, X: base}
}
+// If the result is an identifier, it is not resolved.
func (p *parser) tryVarType(isParam bool) ast.Expr {
if isParam && p.tok == token.ELLIPSIS {
pos := p.pos
p.next()
typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message
- if typ == nil {
+ if typ != nil {
+ p.resolve(typ)
+ } else {
p.error(pos, "'...' parameter is missing type")
typ = &ast.BadExpr{From: pos, To: p.pos}
}
@@ -713,6 +716,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
return p.tryIdentOrType(false)
}
+// If the result is an identifier, it is not resolved.
func (p *parser) parseVarType(isParam bool) ast.Expr {
typ := p.tryVarType(isParam)
if typ == nil {
@@ -724,6 +728,7 @@ func (p *parser) parseVarType(isParam bool) ast.Expr {
return typ
}
+// If any of the results are identifiers, they are not resolved.
func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
if p.trace {
defer un(trace(p, "VarList"))
@@ -744,9 +749,7 @@ func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
}
// if we had a list of identifiers, it must be followed by a type
- if typ = p.tryVarType(isParam); typ != nil {
- p.resolve(typ)
- }
+ typ = p.tryVarType(isParam)
return
}
@@ -756,7 +759,10 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
defer un(trace(p, "ParameterList"))
}
+ // ParameterDecl
list, typ := p.parseVarList(ellipsisOk)
+
+ // analyze case
if typ != nil {
// IdentifierList Type
idents := p.makeIdentList(list)
@@ -765,10 +771,10 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
// Go spec: The scope of an identifier denoting a function
// parameter or result variable is the function body.
p.declare(field, nil, scope, ast.Var, idents...)
+ p.resolve(typ)
if p.tok == token.COMMA {
p.next()
}
-
for p.tok != token.RPAREN && p.tok != token.EOF {
idents := p.parseIdentList()
typ := p.parseVarType(ellipsisOk)
@@ -777,18 +783,18 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
// Go spec: The scope of an identifier denoting a function
// parameter or result variable is the function body.
p.declare(field, nil, scope, ast.Var, idents...)
+ p.resolve(typ)
if !p.atComma("parameter list") {
break
}
p.next()
}
-
} else {
// Type { "," Type } (anonymous parameters)
params = make([]*ast.Field, len(list))
- for i, x := range list {
- p.resolve(x)
- params[i] = &ast.Field{Type: x}
+ for i, typ := range list {
+ p.resolve(typ)
+ params[i] = &ast.Field{Type: typ}
}
}
diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go
index 5e45acd00..1b7a41b1b 100644
--- a/src/pkg/go/parser/parser_test.go
+++ b/src/pkg/go/parser/parser_test.go
@@ -5,10 +5,12 @@
package parser
import (
+ "bytes"
"fmt"
"go/ast"
"go/token"
"os"
+ "strings"
"testing"
)
@@ -25,7 +27,7 @@ func TestParse(t *testing.T) {
for _, filename := range validFiles {
_, err := ParseFile(fset, filename, nil, DeclarationErrors)
if err != nil {
- t.Errorf("ParseFile(%s): %v", filename, err)
+ t.Fatalf("ParseFile(%s): %v", filename, err)
}
}
}
@@ -70,7 +72,7 @@ func TestParseExpr(t *testing.T) {
src := "a + b"
x, err := ParseExpr(src)
if err != nil {
- t.Errorf("ParseExpr(%s): %v", src, err)
+ t.Fatalf("ParseExpr(%s): %v", src, err)
}
// sanity check
if _, ok := x.(*ast.BinaryExpr); !ok {
@@ -81,7 +83,7 @@ func TestParseExpr(t *testing.T) {
src = "a + *"
_, err = ParseExpr(src)
if err == nil {
- t.Errorf("ParseExpr(%s): %v", src, err)
+ t.Fatalf("ParseExpr(%s): %v", src, err)
}
// it must not crash
@@ -93,7 +95,7 @@ func TestParseExpr(t *testing.T) {
func TestColonEqualsScope(t *testing.T) {
f, err := ParseFile(fset, "", `package p; func f() { x, y, z := x, y, z }`, 0)
if err != nil {
- t.Errorf("parse: %s", err)
+ t.Fatal(err)
}
// RHS refers to undefined globals; LHS does not.
@@ -115,7 +117,7 @@ func TestColonEqualsScope(t *testing.T) {
func TestVarScope(t *testing.T) {
f, err := ParseFile(fset, "", `package p; func f() { var x, y, z = x, y, z }`, 0)
if err != nil {
- t.Errorf("parse: %s", err)
+ t.Fatal(err)
}
// RHS refers to undefined globals; LHS does not.
@@ -133,6 +135,67 @@ func TestVarScope(t *testing.T) {
}
}
+func TestUnresolved(t *testing.T) {
+ f, err := ParseFile(fset, "", `
+package p
+//
+func f1a(int)
+func f2a(byte, int, float)
+func f3a(a, b int, c float)
+func f4a(...complex)
+func f5a(a s1a, b ...complex)
+//
+func f1b(*int)
+func f2b([]byte, (int), *float)
+func f3b(a, b *int, c []float)
+func f4b(...*complex)
+func f5b(a s1a, b ...[]complex)
+//
+type s1a struct { int }
+type s2a struct { byte; int; s1a }
+type s3a struct { a, b int; c float }
+//
+type s1b struct { *int }
+type s2b struct { byte; int; *float }
+type s3b struct { a, b *s3b; c []float }
+`, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ want := "int " + // f1a
+ "byte int float " + // f2a
+ "int float " + // f3a
+ "complex " + // f4a
+ "complex " + // f5a
+ //
+ "int " + // f1b
+ "byte int float " + // f2b
+ "int float " + // f3b
+ "complex " + // f4b
+ "complex " + // f5b
+ //
+ "int " + // s1a
+ "byte int " + // s2a
+ "int float " + // s3a
+ //
+ "int " + // s1a
+ "byte int float " + // s2a
+ "float " // s3a
+
+ // collect unresolved identifiers
+ var buf bytes.Buffer
+ for _, u := range f.Unresolved {
+ buf.WriteString(u.Name)
+ buf.WriteByte(' ')
+ }
+ got := buf.String()
+
+ if got != want {
+ t.Errorf("\ngot: %s\nwant: %s", got, want)
+ }
+}
+
var imports = map[string]bool{
`"a"`: true,
"`a`": true,
@@ -177,3 +240,125 @@ func TestImports(t *testing.T) {
}
}
}
+
+func TestCommentGroups(t *testing.T) {
+ f, err := ParseFile(fset, "", `
+package p /* 1a */ /* 1b */ /* 1c */ // 1d
+/* 2a
+*/
+// 2b
+const pi = 3.1415
+/* 3a */ // 3b
+/* 3c */ const e = 2.7182
+
+// Example from issue 3139
+func ExampleCount() {
+ fmt.Println(strings.Count("cheese", "e"))
+ fmt.Println(strings.Count("five", "")) // before & after each rune
+ // Output:
+ // 3
+ // 5
+}
+`, ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expected := [][]string{
+ {"/* 1a */", "/* 1b */", "/* 1c */", "// 1d"},
+ {"/* 2a\n*/", "// 2b"},
+ {"/* 3a */", "// 3b", "/* 3c */"},
+ {"// Example from issue 3139"},
+ {"// before & after each rune"},
+ {"// Output:", "// 3", "// 5"},
+ }
+ if len(f.Comments) != len(expected) {
+ t.Fatalf("got %d comment groups; expected %d", len(f.Comments), len(expected))
+ }
+ for i, exp := range expected {
+ got := f.Comments[i].List
+ if len(got) != len(exp) {
+ t.Errorf("got %d comments in group %d; expected %d", len(got), i, len(exp))
+ continue
+ }
+ for j, exp := range exp {
+ got := got[j].Text
+ if got != exp {
+ t.Errorf("got %q in group %d; expected %q", got, i, exp)
+ }
+ }
+ }
+}
+
+func getField(file *ast.File, fieldname string) *ast.Field {
+ parts := strings.Split(fieldname, ".")
+ for _, d := range file.Decls {
+ if d, ok := d.(*ast.GenDecl); ok && d.Tok == token.TYPE {
+ for _, s := range d.Specs {
+ if s, ok := s.(*ast.TypeSpec); ok && s.Name.Name == parts[0] {
+ if s, ok := s.Type.(*ast.StructType); ok {
+ for _, f := range s.Fields.List {
+ for _, name := range f.Names {
+ if name.Name == parts[1] {
+ return f
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// Don't use ast.CommentGroup.Text() - we want to see exact comment text.
+func commentText(c *ast.CommentGroup) string {
+ var buf bytes.Buffer
+ if c != nil {
+ for _, c := range c.List {
+ buf.WriteString(c.Text)
+ }
+ }
+ return buf.String()
+}
+
+func checkFieldComments(t *testing.T, file *ast.File, fieldname, lead, line string) {
+ f := getField(file, fieldname)
+ if f == nil {
+ t.Fatalf("field not found: %s", fieldname)
+ }
+ if got := commentText(f.Doc); got != lead {
+ t.Errorf("got lead comment %q; expected %q", got, lead)
+ }
+ if got := commentText(f.Comment); got != line {
+ t.Errorf("got line comment %q; expected %q", got, line)
+ }
+}
+
+func TestLeadAndLineComments(t *testing.T) {
+ f, err := ParseFile(fset, "", `
+package p
+type T struct {
+ /* F1 lead comment */
+ //
+ F1 int /* F1 */ // line comment
+ // F2 lead
+ // comment
+ F2 int // F2 line comment
+ // f3 lead comment
+ f3 int // f3 line comment
+}
+`, ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+ checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
+ checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
+ checkFieldComments(t, f, "T.f3", "// f3 lead comment", "// f3 line comment")
+ ast.FileExports(f)
+ checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
+ checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
+ if getField(f, "T.f3") != nil {
+ t.Error("not expected to find T.f3")
+ }
+}
diff --git a/src/pkg/go/printer/nodes.go b/src/pkg/go/printer/nodes.go
index 727d2a371..f13f9a5a8 100644
--- a/src/pkg/go/printer/nodes.go
+++ b/src/pkg/go/printer/nodes.go
@@ -60,8 +60,8 @@ func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (prin
// setComment sets g as the next comment if g != nil and if node comments
// are enabled - this mode is used when printing source code fragments such
-// as exports only. It assumes that there are no other pending comments to
-// intersperse.
+// as exports only. It assumes that there is no pending comment in p.comments
+// and at most one pending comment in the p.comment cache.
func (p *printer) setComment(g *ast.CommentGroup) {
if g == nil || !p.useNodeComments {
return
@@ -74,10 +74,19 @@ func (p *printer) setComment(g *ast.CommentGroup) {
// should never happen - handle gracefully and flush
// all comments up to g, ignore anything after that
p.flush(p.posFor(g.List[0].Pos()), token.ILLEGAL)
+ p.comments = p.comments[0:1]
+ // in debug mode, report error
+ p.internalError("setComment found pending comments")
}
p.comments[0] = g
p.cindex = 0
- p.nextComment() // get comment ready for use
+ // don't overwrite any pending comment in the p.comment cache
+ // (there may be a pending comment when a line comment is
+ // immediately followed by a lead comment with no other
+ // tokens inbetween)
+ if p.commentOffset == infinity {
+ p.nextComment() // get comment ready for use
+ }
}
type exprListMode uint
diff --git a/src/pkg/log/log.go b/src/pkg/log/log.go
index 1d7f209d1..d37e4375e 100644
--- a/src/pkg/log/log.go
+++ b/src/pkg/log/log.go
@@ -26,7 +26,7 @@ const (
// Bits or'ed together to control what's printed. There is no control over the
// order they appear (the order listed here) or the format they present (as
// described in the comments). A colon appears after these items:
- // 2009/0123 01:23:23.123123 /a/b/c/d.go:23: message
+ // 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
Ldate = 1 << iota // the date: 2009/01/23
Ltime // the time: 01:23:23
Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
diff --git a/src/pkg/math/big/nat.go b/src/pkg/math/big/nat.go
index 0bc6572b9..eaa6ff066 100644
--- a/src/pkg/math/big/nat.go
+++ b/src/pkg/math/big/nat.go
@@ -271,10 +271,10 @@ func karatsuba(z, x, y nat) {
// xd = x1 - x0
// yd = y0 - y1
//
- // z1 = xd*yd + z1 + z0
- // = (x1-x0)*(y0 - y1) + z1 + z0
- // = x1*y0 - x1*y1 - x0*y0 + x0*y1 + z1 + z0
- // = x1*y0 - z1 - z0 + x0*y1 + z1 + z0
+ // z1 = xd*yd + z2 + z0
+ // = (x1-x0)*(y0 - y1) + z2 + z0
+ // = x1*y0 - x1*y1 - x0*y0 + x0*y1 + z2 + z0
+ // = x1*y0 - z2 - z0 + x0*y1 + z2 + z0
// = x1*y0 + x0*y1
// split x, y into "digits"
@@ -318,7 +318,7 @@ func karatsuba(z, x, y nat) {
// save original z2:z0
// (ok to use upper half of z since we're done recursing)
r := z[n*4:]
- copy(r, z)
+ copy(r, z[:n*2])
// add up all partial products
//
diff --git a/src/pkg/math/big/nat_test.go b/src/pkg/math/big/nat_test.go
index 7f3f76dc3..becde5d17 100644
--- a/src/pkg/math/big/nat_test.go
+++ b/src/pkg/math/big/nat_test.go
@@ -661,3 +661,21 @@ func TestExpNN(t *testing.T) {
}
}
}
+
+func ExpHelper(b *testing.B, x, y Word) {
+ var z nat
+ for i := 0; i < b.N; i++ {
+ z.expWW(x, y)
+ }
+}
+
+func BenchmarkExp3Power0x10(b *testing.B) { ExpHelper(b, 3, 0x10) }
+func BenchmarkExp3Power0x40(b *testing.B) { ExpHelper(b, 3, 0x40) }
+func BenchmarkExp3Power0x100(b *testing.B) { ExpHelper(b, 3, 0x100) }
+func BenchmarkExp3Power0x400(b *testing.B) { ExpHelper(b, 3, 0x400) }
+func BenchmarkExp3Power0x1000(b *testing.B) { ExpHelper(b, 3, 0x1000) }
+func BenchmarkExp3Power0x4000(b *testing.B) { ExpHelper(b, 3, 0x4000) }
+func BenchmarkExp3Power0x10000(b *testing.B) { ExpHelper(b, 3, 0x10000) }
+func BenchmarkExp3Power0x40000(b *testing.B) { ExpHelper(b, 3, 0x40000) }
+func BenchmarkExp3Power0x100000(b *testing.B) { ExpHelper(b, 3, 0x100000) }
+func BenchmarkExp3Power0x400000(b *testing.B) { ExpHelper(b, 3, 0x400000) }
diff --git a/src/pkg/mime/multipart/multipart.go b/src/pkg/mime/multipart/multipart.go
index 6ace4be56..e9e337b92 100644
--- a/src/pkg/mime/multipart/multipart.go
+++ b/src/pkg/mime/multipart/multipart.go
@@ -22,11 +22,6 @@ import (
"net/textproto"
)
-// TODO(bradfitz): inline these once the compiler can inline them in
-// read-only situation (such as bytes.HasSuffix)
-var lf = []byte("\n")
-var crlf = []byte("\r\n")
-
var emptyParams = make(map[string]string)
// A Part represents a single part in a multipart body.
@@ -36,8 +31,9 @@ type Part struct {
// i.e. "foo-bar" changes case to "Foo-Bar"
Header textproto.MIMEHeader
- buffer *bytes.Buffer
- mr *Reader
+ buffer *bytes.Buffer
+ mr *Reader
+ bytesRead int
disposition string
dispositionParams map[string]string
@@ -113,14 +109,26 @@ func (bp *Part) populateHeaders() error {
// Read reads the body of a part, after its headers and before the
// next part (if any) begins.
func (p *Part) Read(d []byte) (n int, err error) {
+ defer func() {
+ p.bytesRead += n
+ }()
if p.buffer.Len() >= len(d) {
// Internal buffer of unconsumed data is large enough for
// the read request. No need to parse more at the moment.
return p.buffer.Read(d)
}
peek, err := p.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessor
- unexpectedEof := err == io.EOF
- if err != nil && !unexpectedEof {
+
+ // Look for an immediate empty part without a leading \r\n
+ // before the boundary separator. Some MIME code makes empty
+ // parts like this. Most browsers, however, write the \r\n
+ // before the subsequent boundary even for empty parts and
+ // won't hit this path.
+ if p.bytesRead == 0 && p.mr.peekBufferIsEmptyPart(peek) {
+ return 0, io.EOF
+ }
+ unexpectedEOF := err == io.EOF
+ if err != nil && !unexpectedEOF {
return 0, fmt.Errorf("multipart: Part Read: %v", err)
}
if peek == nil {
@@ -138,7 +146,7 @@ func (p *Part) Read(d []byte) (n int, err error) {
foundBoundary = true
} else if safeCount := len(peek) - len(p.mr.nlDashBoundary); safeCount > 0 {
nCopy = safeCount
- } else if unexpectedEof {
+ } else if unexpectedEOF {
// If we've run out of peek buffer and the boundary
// wasn't found (and can't possibly fit), we must have
// hit the end of the file unexpectedly.
@@ -172,7 +180,10 @@ type Reader struct {
currentPart *Part
partsRead int
- nl, nlDashBoundary, dashBoundaryDash, dashBoundary []byte
+ nl []byte // "\r\n" or "\n" (set after seeing first boundary line)
+ nlDashBoundary []byte // nl + "--boundary"
+ dashBoundaryDash []byte // "--boundary--"
+ dashBoundary []byte // "--boundary"
}
// NextPart returns the next part in the multipart or an error.
@@ -185,7 +196,7 @@ func (r *Reader) NextPart() (*Part, error) {
expectNewPart := false
for {
line, err := r.bufReader.ReadSlice('\n')
- if err == io.EOF && bytes.Equal(line, r.dashBoundaryDash) {
+ if err == io.EOF && r.isFinalBoundary(line) {
// If the buffer ends in "--boundary--" without the
// trailing "\r\n", ReadSlice will return an error
// (since it's missing the '\n'), but this is a valid
@@ -207,7 +218,7 @@ func (r *Reader) NextPart() (*Part, error) {
return bp, nil
}
- if hasPrefixThenNewline(line, r.dashBoundaryDash) {
+ if r.isFinalBoundary(line) {
// Expected EOF
return nil, io.EOF
}
@@ -235,7 +246,19 @@ func (r *Reader) NextPart() (*Part, error) {
panic("unreachable")
}
-func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {
+// isFinalBoundary returns whether line is the final boundary line
+// indiciating that all parts are over.
+// It matches `^--boundary--[ \t]*(\r\n)?$`
+func (mr *Reader) isFinalBoundary(line []byte) bool {
+ if !bytes.HasPrefix(line, mr.dashBoundaryDash) {
+ return false
+ }
+ rest := line[len(mr.dashBoundaryDash):]
+ rest = skipLWSPChar(rest)
+ return len(rest) == 0 || bytes.Equal(rest, mr.nl)
+}
+
+func (mr *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) {
// http://tools.ietf.org/html/rfc2046#section-5.1
// The boundary delimiter line is then defined as a line
// consisting entirely of two hyphen characters ("-",
@@ -245,32 +268,52 @@ func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {
if !bytes.HasPrefix(line, mr.dashBoundary) {
return false
}
- if bytes.HasSuffix(line, mr.nl) {
- return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-len(mr.nl)])
+ rest := line[len(mr.dashBoundary):]
+ rest = skipLWSPChar(rest)
+
+ // On the first part, see our lines are ending in \n instead of \r\n
+ // and switch into that mode if so. This is a violation of the spec,
+ // but occurs in practice.
+ if mr.partsRead == 0 && len(rest) == 1 && rest[0] == '\n' {
+ mr.nl = mr.nl[1:]
+ mr.nlDashBoundary = mr.nlDashBoundary[1:]
}
- // Violate the spec and also support newlines without the
- // carriage return...
- if mr.partsRead == 0 && bytes.HasSuffix(line, lf) {
- if onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1]) {
- mr.nl = mr.nl[1:]
- mr.nlDashBoundary = mr.nlDashBoundary[1:]
- return true
- }
- }
- return false
+ return bytes.Equal(rest, mr.nl)
}
-func onlyHorizontalWhitespace(s []byte) bool {
- for _, b := range s {
- if b != ' ' && b != '\t' {
- return false
- }
+// peekBufferIsEmptyPart returns whether the provided peek-ahead
+// buffer represents an empty part. This is only called if we've not
+// already read any bytes in this part and checks for the case of MIME
+// software not writing the \r\n on empty parts. Some does, some
+// doesn't.
+//
+// This checks that what follows the "--boundary" is actually the end
+// ("--boundary--" with optional whitespace) or optional whitespace
+// and then a newline, so we don't catch "--boundaryFAKE", in which
+// case the whole line is part of the data.
+func (mr *Reader) peekBufferIsEmptyPart(peek []byte) bool {
+ // End of parts case.
+ // Test whether peek matches `^--boundary--[ \t]*(?:\r\n|$)`
+ if bytes.HasPrefix(peek, mr.dashBoundaryDash) {
+ rest := peek[len(mr.dashBoundaryDash):]
+ rest = skipLWSPChar(rest)
+ return bytes.HasPrefix(rest, mr.nl) || len(rest) == 0
}
- return true
+ if !bytes.HasPrefix(peek, mr.dashBoundary) {
+ return false
+ }
+ // Test whether rest matches `^[ \t]*\r\n`)
+ rest := peek[len(mr.dashBoundary):]
+ rest = skipLWSPChar(rest)
+ return bytes.HasPrefix(rest, mr.nl)
}
-func hasPrefixThenNewline(s, prefix []byte) bool {
- return bytes.HasPrefix(s, prefix) &&
- (len(s) == len(prefix)+1 && s[len(s)-1] == '\n' ||
- len(s) == len(prefix)+2 && bytes.HasSuffix(s, crlf))
+// skipLWSPChar returns b with leading spaces and tabs removed.
+// RFC 822 defines:
+// LWSP-char = SPACE / HTAB
+func skipLWSPChar(b []byte) []byte {
+ for len(b) > 0 && (b[0] == ' ' || b[0] == '\t') {
+ b = b[1:]
+ }
+ return b
}
diff --git a/src/pkg/mime/multipart/multipart_test.go b/src/pkg/mime/multipart/multipart_test.go
index ca7108d7a..cd65e177e 100644
--- a/src/pkg/mime/multipart/multipart_test.go
+++ b/src/pkg/mime/multipart/multipart_test.go
@@ -10,20 +10,13 @@ import (
"fmt"
"io"
"io/ioutil"
+ "net/textproto"
"os"
+ "reflect"
"strings"
"testing"
)
-func TestHorizontalWhitespace(t *testing.T) {
- if !onlyHorizontalWhitespace([]byte(" \t")) {
- t.Error("expected pass")
- }
- if onlyHorizontalWhitespace([]byte("foo bar")) {
- t.Error("expected failure")
- }
-}
-
func TestBoundaryLine(t *testing.T) {
mr := NewReader(strings.NewReader(""), "myBoundary")
if !mr.isBoundaryDelimiterLine([]byte("--myBoundary\r\n")) {
@@ -319,29 +312,6 @@ Oh no, premature EOF!
}
}
-func TestZeroLengthBody(t *testing.T) {
- testBody := strings.Replace(`
-This is a multi-part message. This line is ignored.
---MyBoundary
-foo: bar
-
-
---MyBoundary--
-`, "\n", "\r\n", -1)
- r := NewReader(strings.NewReader(testBody), "MyBoundary")
- part, err := r.NextPart()
- if err != nil {
- t.Fatalf("didn't get a part")
- }
- n, err := io.Copy(ioutil.Discard, part)
- if err != nil {
- t.Errorf("error reading part: %v", err)
- }
- if n != 0 {
- t.Errorf("read %d bytes; expected 0", n)
- }
-}
-
type slowReader struct {
r io.Reader
}
@@ -427,3 +397,214 @@ func TestNested(t *testing.T) {
t.Fatalf("final outer NextPart = %v; want io.EOF", err)
}
}
+
+type headerBody struct {
+ header textproto.MIMEHeader
+ body string
+}
+
+func formData(key, value string) headerBody {
+ return headerBody{
+ textproto.MIMEHeader{
+ "Content-Type": {"text/plain; charset=ISO-8859-1"},
+ "Content-Disposition": {"form-data; name=" + key},
+ },
+ value,
+ }
+}
+
+type parseTest struct {
+ name string
+ in, sep string
+ want []headerBody
+}
+
+var parseTests = []parseTest{
+ // Actual body from App Engine on a blob upload. The final part (the
+ // Content-Type: message/external-body) is what App Engine replaces
+ // the uploaded file with. The other form fields (prefixed with
+ // "other" in their form-data name) are unchanged. A bug was
+ // reported with blob uploads failing when the other fields were
+ // empty. This was the MIME POST body that previously failed.
+ {
+ name: "App Engine post",
+ sep: "00151757727e9583fd04bfbca4c6",
+ in: "--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherEmpty1\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherFoo1\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherFoo2\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherEmpty2\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatFoo\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatFoo\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatEmpty\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatEmpty\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=submit\r\n\r\nSubmit\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: message/external-body; charset=ISO-8859-1; blob-key=AHAZQqG84qllx7HUqO_oou5EvdYQNS3Mbbkb0RjjBoM_Kc1UqEN2ygDxWiyCPulIhpHRPx-VbpB6RX4MrsqhWAi_ZxJ48O9P2cTIACbvATHvg7IgbvZytyGMpL7xO1tlIvgwcM47JNfv_tGhy1XwyEUO8oldjPqg5Q\r\nContent-Disposition: form-data; name=file; filename=\"fall.png\"\r\n\r\nContent-Type: image/png\r\nContent-Length: 232303\r\nX-AppEngine-Upload-Creation: 2012-05-10 23:14:02.715173\r\nContent-MD5: MzRjODU1ZDZhZGU1NmRlOWEwZmMwMDdlODBmZTA0NzA=\r\nContent-Disposition: form-data; name=file; filename=\"fall.png\"\r\n\r\n\r\n--00151757727e9583fd04bfbca4c6--",
+ want: []headerBody{
+ formData("otherEmpty1", ""),
+ formData("otherFoo1", "foo"),
+ formData("otherFoo2", "foo"),
+ formData("otherEmpty2", ""),
+ formData("otherRepeatFoo", "foo"),
+ formData("otherRepeatFoo", "foo"),
+ formData("otherRepeatEmpty", ""),
+ formData("otherRepeatEmpty", ""),
+ formData("submit", "Submit"),
+ {textproto.MIMEHeader{
+ "Content-Type": {"message/external-body; charset=ISO-8859-1; blob-key=AHAZQqG84qllx7HUqO_oou5EvdYQNS3Mbbkb0RjjBoM_Kc1UqEN2ygDxWiyCPulIhpHRPx-VbpB6RX4MrsqhWAi_ZxJ48O9P2cTIACbvATHvg7IgbvZytyGMpL7xO1tlIvgwcM47JNfv_tGhy1XwyEUO8oldjPqg5Q"},
+ "Content-Disposition": {"form-data; name=file; filename=\"fall.png\""},
+ }, "Content-Type: image/png\r\nContent-Length: 232303\r\nX-AppEngine-Upload-Creation: 2012-05-10 23:14:02.715173\r\nContent-MD5: MzRjODU1ZDZhZGU1NmRlOWEwZmMwMDdlODBmZTA0NzA=\r\nContent-Disposition: form-data; name=file; filename=\"fall.png\"\r\n\r\n"},
+ },
+ },
+
+ // Single empty part, ended with --boundary immediately after headers.
+ {
+ name: "single empty part, --boundary",
+ sep: "abc",
+ in: "--abc\r\nFoo: bar\r\n\r\n--abc--",
+ want: []headerBody{
+ {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
+ },
+ },
+
+ // Single empty part, ended with \r\n--boundary immediately after headers.
+ {
+ name: "single empty part, \r\n--boundary",
+ sep: "abc",
+ in: "--abc\r\nFoo: bar\r\n\r\n\r\n--abc--",
+ want: []headerBody{
+ {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
+ },
+ },
+
+ // Final part empty.
+ {
+ name: "final part empty",
+ sep: "abc",
+ in: "--abc\r\nFoo: bar\r\n\r\n--abc\r\nFoo2: bar2\r\n\r\n--abc--",
+ want: []headerBody{
+ {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
+ {textproto.MIMEHeader{"Foo2": {"bar2"}}, ""},
+ },
+ },
+
+ // Final part empty with newlines after final separator.
+ {
+ name: "final part empty then crlf",
+ sep: "abc",
+ in: "--abc\r\nFoo: bar\r\n\r\n--abc--\r\n",
+ want: []headerBody{
+ {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
+ },
+ },
+
+ // Final part empty with lwsp-chars after final separator.
+ {
+ name: "final part empty then lwsp",
+ sep: "abc",
+ in: "--abc\r\nFoo: bar\r\n\r\n--abc-- \t",
+ want: []headerBody{
+ {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
+ },
+ },
+
+ // No parts (empty form as submitted by Chrome)
+ {
+ name: "no parts",
+ sep: "----WebKitFormBoundaryQfEAfzFOiSemeHfA",
+ in: "------WebKitFormBoundaryQfEAfzFOiSemeHfA--\r\n",
+ want: []headerBody{},
+ },
+
+ // Part containing data starting with the boundary, but with additional suffix.
+ {
+ name: "fake separator as data",
+ sep: "sep",
+ in: "--sep\r\nFoo: bar\r\n\r\n--sepFAKE\r\n--sep--",
+ want: []headerBody{
+ {textproto.MIMEHeader{"Foo": {"bar"}}, "--sepFAKE"},
+ },
+ },
+
+ // Part containing a boundary with whitespace following it.
+ {
+ name: "boundary with whitespace",
+ sep: "sep",
+ in: "--sep \r\nFoo: bar\r\n\r\ntext\r\n--sep--",
+ want: []headerBody{
+ {textproto.MIMEHeader{"Foo": {"bar"}}, "text"},
+ },
+ },
+
+ // With ignored leading line.
+ {
+ name: "leading line",
+ sep: "MyBoundary",
+ in: strings.Replace(`This is a multi-part message. This line is ignored.
+--MyBoundary
+foo: bar
+
+
+--MyBoundary--`, "\n", "\r\n", -1),
+ want: []headerBody{
+ {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
+ },
+ },
+
+ roundTripParseTest(),
+}
+
+func TestParse(t *testing.T) {
+Cases:
+ for _, tt := range parseTests {
+ r := NewReader(strings.NewReader(tt.in), tt.sep)
+ got := []headerBody{}
+ for {
+ p, err := r.NextPart()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Errorf("in test %q, NextPart: %v", tt.name, err)
+ continue Cases
+ }
+ pbody, err := ioutil.ReadAll(p)
+ if err != nil {
+ t.Errorf("in test %q, error reading part: %v", tt.name, err)
+ continue Cases
+ }
+ got = append(got, headerBody{p.Header, string(pbody)})
+ }
+ if !reflect.DeepEqual(tt.want, got) {
+ t.Errorf("test %q:\n got: %v\nwant: %v", tt.name, got, tt.want)
+ if len(tt.want) != len(got) {
+ t.Errorf("test %q: got %d parts, want %d", tt.name, len(got), len(tt.want))
+ } else if len(got) > 1 {
+ for pi, wantPart := range tt.want {
+ if !reflect.DeepEqual(wantPart, got[pi]) {
+ t.Errorf("test %q, part %d:\n got: %v\nwant: %v", tt.name, pi, got[pi], wantPart)
+ }
+ }
+ }
+ }
+ }
+}
+
+func roundTripParseTest() parseTest {
+ t := parseTest{
+ name: "round trip",
+ want: []headerBody{
+ formData("empty", ""),
+ formData("lf", "\n"),
+ formData("cr", "\r"),
+ formData("crlf", "\r\n"),
+ formData("foo", "bar"),
+ },
+ }
+ var buf bytes.Buffer
+ w := NewWriter(&buf)
+ for _, p := range t.want {
+ pw, err := w.CreatePart(p.header)
+ if err != nil {
+ panic(err)
+ }
+ _, err = pw.Write([]byte(p.body))
+ if err != nil {
+ panic(err)
+ }
+ }
+ w.Close()
+ t.in = buf.String()
+ t.sep = w.Boundary()
+ return t
+}
diff --git a/src/pkg/net/file.go b/src/pkg/net/file.go
index c95d16d64..fc6c6fad8 100644
--- a/src/pkg/net/file.go
+++ b/src/pkg/net/file.go
@@ -89,8 +89,8 @@ func FileConn(f *os.File) (c Conn, err error) {
// FileListener returns a copy of the network listener corresponding
// to the open file f. It is the caller's responsibility to close l
-// when finished. Closing c does not affect l, and closing l does not
-// affect c.
+// when finished. Closing l does not affect f, and closing f does not
+// affect l.
func FileListener(f *os.File) (l Listener, err error) {
fd, err := newFileFD(f)
if err != nil {
diff --git a/src/pkg/net/http/client.go b/src/pkg/net/http/client.go
index 5d450258b..54564e098 100644
--- a/src/pkg/net/http/client.go
+++ b/src/pkg/net/http/client.go
@@ -278,6 +278,11 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response,
return nil, err
}
req.Header.Set("Content-Type", bodyType)
+ if c.Jar != nil {
+ for _, cookie := range c.Jar.Cookies(req.URL) {
+ req.AddCookie(cookie)
+ }
+ }
r, err = send(req, c.Transport)
if err == nil && c.Jar != nil {
c.Jar.SetCookies(req.URL, r.Cookies())
diff --git a/src/pkg/net/http/client_test.go b/src/pkg/net/http/client_test.go
index e00b62e59..9b4261b9f 100644
--- a/src/pkg/net/http/client_test.go
+++ b/src/pkg/net/http/client_test.go
@@ -256,6 +256,31 @@ var echoCookiesRedirectHandler = HandlerFunc(func(w ResponseWriter, r *Request)
}
})
+func TestClientSendsCookieFromJar(t *testing.T) {
+ tr := &recordingTransport{}
+ client := &Client{Transport: tr}
+ client.Jar = &TestJar{perURL: make(map[string][]*Cookie)}
+ us := "http://dummy.faketld/"
+ u, _ := url.Parse(us)
+ client.Jar.SetCookies(u, expectedCookies)
+
+ client.Get(us) // Note: doesn't hit network
+ matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
+
+ client.Head(us) // Note: doesn't hit network
+ matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
+
+ client.Post(us, "text/plain", strings.NewReader("body")) // Note: doesn't hit network
+ matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
+
+ client.PostForm(us, url.Values{}) // Note: doesn't hit network
+ matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
+
+ req, _ := NewRequest("GET", us, nil)
+ client.Do(req) // Note: doesn't hit network
+ matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
+}
+
// Just enough correctness for our redirect tests. Uses the URL.Host as the
// scope of all cookies.
type TestJar struct {
diff --git a/src/pkg/net/http/proxy_test.go b/src/pkg/net/http/proxy_test.go
index 9b320b3aa..5ecffafac 100644
--- a/src/pkg/net/http/proxy_test.go
+++ b/src/pkg/net/http/proxy_test.go
@@ -5,6 +5,7 @@
package http
import (
+ "net/url"
"os"
"testing"
)
@@ -46,3 +47,32 @@ func TestUseProxy(t *testing.T) {
}
}
}
+
+var cacheKeysTests = []struct {
+ proxy string
+ scheme string
+ addr string
+ key string
+}{
+ {"", "http", "foo.com", "|http|foo.com"},
+ {"", "https", "foo.com", "|https|foo.com"},
+ {"http://foo.com", "http", "foo.com", "http://foo.com|http|"},
+ {"http://foo.com", "https", "foo.com", "http://foo.com|https|foo.com"},
+}
+
+func TestCacheKeys(t *testing.T) {
+ for _, tt := range cacheKeysTests {
+ var proxy *url.URL
+ if tt.proxy != "" {
+ u, err := url.Parse(tt.proxy)
+ if err != nil {
+ t.Fatal(err)
+ }
+ proxy = u
+ }
+ cm := connectMethod{proxy, tt.scheme, tt.addr}
+ if cm.String() != tt.key {
+ t.Fatalf("{%q, %q, %q} cache key %q; want %q", tt.proxy, tt.scheme, tt.addr, cm.String(), tt.key)
+ }
+ }
+}
diff --git a/src/pkg/net/http/response.go b/src/pkg/net/http/response.go
index b79022097..945ecd8a4 100644
--- a/src/pkg/net/http/response.go
+++ b/src/pkg/net/http/response.go
@@ -202,9 +202,12 @@ func (r *Response) Write(w io.Writer) error {
text = "status code " + strconv.Itoa(r.StatusCode)
}
}
- io.WriteString(w, "HTTP/"+strconv.Itoa(r.ProtoMajor)+".")
- io.WriteString(w, strconv.Itoa(r.ProtoMinor)+" ")
- io.WriteString(w, strconv.Itoa(r.StatusCode)+" "+text+"\r\n")
+ protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)
+ statusCode := strconv.Itoa(r.StatusCode) + " "
+ if strings.HasPrefix(text, statusCode) {
+ text = text[len(statusCode):]
+ }
+ io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n")
// Process Body,ContentLength,Close,Trailer
tw, err := newTransferWriter(r)
diff --git a/src/pkg/net/http/response_test.go b/src/pkg/net/http/response_test.go
index 165ec3624..6eed4887d 100644
--- a/src/pkg/net/http/response_test.go
+++ b/src/pkg/net/http/response_test.go
@@ -14,6 +14,7 @@ import (
"io/ioutil"
"net/url"
"reflect"
+ "strings"
"testing"
)
@@ -444,3 +445,17 @@ func TestLocationResponse(t *testing.T) {
}
}
}
+
+func TestResponseStatusStutter(t *testing.T) {
+ r := &Response{
+ Status: "123 some status",
+ StatusCode: 123,
+ ProtoMajor: 1,
+ ProtoMinor: 3,
+ }
+ var buf bytes.Buffer
+ r.Write(&buf)
+ if strings.Contains(buf.String(), "123 123") {
+ t.Errorf("stutter in status: %s", buf.String())
+ }
+}
diff --git a/src/pkg/net/http/server.go b/src/pkg/net/http/server.go
index 924ffd348..0572b4ae3 100644
--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -31,7 +31,7 @@ import (
// Errors introduced by the HTTP server.
var (
ErrWriteAfterFlush = errors.New("Conn.Write called after Flush")
- ErrBodyNotAllowed = errors.New("http: response status code does not allow body")
+ ErrBodyNotAllowed = errors.New("http: request method or response status code does not allow body")
ErrHijacked = errors.New("Conn has been hijacked")
ErrContentLength = errors.New("Conn.Write wrote more than the declared Content-Length")
)
diff --git a/src/pkg/net/http/transfer.go b/src/pkg/net/http/transfer.go
index 3c8fe7f5b..9e9d84172 100644
--- a/src/pkg/net/http/transfer.go
+++ b/src/pkg/net/http/transfer.go
@@ -71,7 +71,9 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) {
}
}
case *Response:
- t.Method = rr.Request.Method
+ if rr.Request != nil {
+ t.Method = rr.Request.Method
+ }
t.Body = rr.Body
t.BodyCloser = rr.Body
t.ContentLength = rr.ContentLength
@@ -79,7 +81,7 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) {
t.TransferEncoding = rr.TransferEncoding
t.Trailer = rr.Trailer
atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
- t.ResponseToHEAD = noBodyExpected(rr.Request.Method)
+ t.ResponseToHEAD = noBodyExpected(t.Method)
}
// Sanitize Body,ContentLength,TransferEncoding
diff --git a/src/pkg/net/http/transport.go b/src/pkg/net/http/transport.go
index 024975946..6efe191eb 100644
--- a/src/pkg/net/http/transport.go
+++ b/src/pkg/net/http/transport.go
@@ -450,10 +450,14 @@ type connectMethod struct {
func (ck *connectMethod) String() string {
proxyStr := ""
+ targetAddr := ck.targetAddr
if ck.proxyURL != nil {
proxyStr = ck.proxyURL.String()
+ if ck.targetScheme == "http" {
+ targetAddr = ""
+ }
}
- return strings.Join([]string{proxyStr, ck.targetScheme, ck.targetAddr}, "|")
+ return strings.Join([]string{proxyStr, ck.targetScheme, targetAddr}, "|")
}
// addr returns the first hop "host:port" to which we need to TCP connect.
diff --git a/src/pkg/net/mail/message.go b/src/pkg/net/mail/message.go
index 0917bbedf..b610ccf3f 100644
--- a/src/pkg/net/mail/message.go
+++ b/src/pkg/net/mail/message.go
@@ -69,11 +69,12 @@ var dateLayouts []string
func init() {
// Generate layouts based on RFC 5322, section 3.3.
- dows := [...]string{"", "Mon, "} // day-of-week
- days := [...]string{"2", "02"} // day = 1*2DIGIT
- years := [...]string{"2006", "06"} // year = 4*DIGIT / 2*DIGIT
- seconds := [...]string{":05", ""} // second
- zones := [...]string{"-0700", "MST"} // zone = (("+" / "-") 4DIGIT) / "GMT" / ...
+ dows := [...]string{"", "Mon, "} // day-of-week
+ days := [...]string{"2", "02"} // day = 1*2DIGIT
+ years := [...]string{"2006", "06"} // year = 4*DIGIT / 2*DIGIT
+ seconds := [...]string{":05", ""} // second
+ // "-0700 (MST)" is not in RFC 5322, but is common.
+ zones := [...]string{"-0700", "MST", "-0700 (MST)"} // zone = (("+" / "-") 4DIGIT) / "GMT" / ...
for _, dow := range dows {
for _, day := range days {
diff --git a/src/pkg/net/mail/message_test.go b/src/pkg/net/mail/message_test.go
index 671ff2efa..fd17eb414 100644
--- a/src/pkg/net/mail/message_test.go
+++ b/src/pkg/net/mail/message_test.go
@@ -95,6 +95,11 @@ func TestDateParsing(t *testing.T) {
"21 Nov 97 09:55:06 GMT",
time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("GMT", 0)),
},
+ // Commonly found format not specified by RFC 5322.
+ {
+ "Fri, 21 Nov 1997 09:55:06 -0600 (MDT)",
+ time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
+ },
}
for _, test := range tests {
hdr := Header{
diff --git a/src/pkg/net/url/url.go b/src/pkg/net/url/url.go
index b6e79adc2..17bf0d3a3 100644
--- a/src/pkg/net/url/url.go
+++ b/src/pkg/net/url/url.go
@@ -401,11 +401,12 @@ Error:
}
func parseAuthority(authority string) (user *Userinfo, host string, err error) {
- if strings.Index(authority, "@") < 0 {
+ i := strings.LastIndex(authority, "@")
+ if i < 0 {
host = authority
return
}
- userinfo, host := split(authority, '@', true)
+ userinfo, host := authority[:i], authority[i+1:]
if strings.Index(userinfo, ":") < 0 {
if userinfo, err = unescape(userinfo, encodeUserPassword); err != nil {
return
diff --git a/src/pkg/net/url/url_test.go b/src/pkg/net/url/url_test.go
index d8b253142..75e8abe4e 100644
--- a/src/pkg/net/url/url_test.go
+++ b/src/pkg/net/url/url_test.go
@@ -188,6 +188,37 @@ var urltests = []URLTest{
},
"http://user:password@google.com",
},
+ // unescaped @ in username should not confuse host
+ {
+ "http://j@ne:password@google.com",
+ &URL{
+ Scheme: "http",
+ User: UserPassword("j@ne", "password"),
+ Host: "google.com",
+ },
+ "http://j%40ne:password@google.com",
+ },
+ // unescaped @ in password should not confuse host
+ {
+ "http://jane:p@ssword@google.com",
+ &URL{
+ Scheme: "http",
+ User: UserPassword("jane", "p@ssword"),
+ Host: "google.com",
+ },
+ "http://jane:p%40ssword@google.com",
+ },
+ {
+ "http://j@ne:password@google.com/p@th?q=@go",
+ &URL{
+ Scheme: "http",
+ User: UserPassword("j@ne", "password"),
+ Host: "google.com",
+ Path: "/p@th",
+ RawQuery: "q=@go",
+ },
+ "http://j%40ne:password@google.com/p@th?q=@go",
+ },
{
"http://www.google.com/?q=go+language#foo",
&URL{
diff --git a/src/pkg/os/exec/exec.go b/src/pkg/os/exec/exec.go
index bbd04902b..9a8e18170 100644
--- a/src/pkg/os/exec/exec.go
+++ b/src/pkg/os/exec/exec.go
@@ -204,6 +204,12 @@ func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) {
return pw, nil
}
+func (c *Cmd) closeDescriptors(closers []io.Closer) {
+ for _, fd := range closers {
+ fd.Close()
+ }
+}
+
// Run starts the specified command and waits for it to complete.
//
// The returned error is nil if the command runs, has no problems
@@ -233,6 +239,8 @@ func (c *Cmd) Start() error {
for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {
fd, err := setupFd(c)
if err != nil {
+ c.closeDescriptors(c.closeAfterStart)
+ c.closeDescriptors(c.closeAfterWait)
return err
}
c.childFiles = append(c.childFiles, fd)
@@ -247,12 +255,12 @@ func (c *Cmd) Start() error {
Sys: c.SysProcAttr,
})
if err != nil {
+ c.closeDescriptors(c.closeAfterStart)
+ c.closeDescriptors(c.closeAfterWait)
return err
}
- for _, fd := range c.closeAfterStart {
- fd.Close()
- }
+ c.closeDescriptors(c.closeAfterStart)
c.errch = make(chan error, len(c.goroutine))
for _, fn := range c.goroutine {
@@ -301,9 +309,7 @@ func (c *Cmd) Wait() error {
}
}
- for _, fd := range c.closeAfterWait {
- fd.Close()
- }
+ c.closeDescriptors(c.closeAfterWait)
if err != nil {
return err
diff --git a/src/pkg/path/filepath/path.go b/src/pkg/path/filepath/path.go
index a4e429bae..815021bd0 100644
--- a/src/pkg/path/filepath/path.go
+++ b/src/pkg/path/filepath/path.go
@@ -320,8 +320,11 @@ func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
}
for _, fileInfo := range list {
- if err = walk(Join(path, fileInfo.Name()), fileInfo, walkFn); err != nil {
- return err
+ err = walk(Join(path, fileInfo.Name()), fileInfo, walkFn)
+ if err != nil {
+ if !fileInfo.IsDir() || err != SkipDir {
+ return err
+ }
}
}
return nil
diff --git a/src/pkg/path/filepath/path_plan9.go b/src/pkg/path/filepath/path_plan9.go
index cf028a75c..59a5812dd 100644
--- a/src/pkg/path/filepath/path_plan9.go
+++ b/src/pkg/path/filepath/path_plan9.go
@@ -12,7 +12,7 @@ func IsAbs(path string) bool {
}
// VolumeName returns the leading volume name on Windows.
-// It returns "" elsewhere
+// It returns "" elsewhere.
func VolumeName(path string) string {
return ""
}
diff --git a/src/pkg/path/filepath/path_test.go b/src/pkg/path/filepath/path_test.go
index b8766588c..070905fd3 100644
--- a/src/pkg/path/filepath/path_test.go
+++ b/src/pkg/path/filepath/path_test.go
@@ -869,3 +869,29 @@ func TestDriveLetterInEvalSymlinks(t *testing.T) {
t.Errorf("Results of EvalSymlinks do not match: %q and %q", flp, fup)
}
}
+
+func TestBug3486(t *testing.T) { // http://code.google.com/p/go/issues/detail?id=3486
+ root, err := filepath.EvalSymlinks(os.Getenv("GOROOT"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ lib := filepath.Join(root, "lib")
+ src := filepath.Join(root, "src")
+ seenSrc := false
+ filepath.Walk(root, func(pth string, info os.FileInfo, err error) error {
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ switch pth {
+ case lib:
+ return filepath.SkipDir
+ case src:
+ seenSrc = true
+ }
+ return nil
+ })
+ if !seenSrc {
+ t.Fatalf("%q not seen", src)
+ }
+}
diff --git a/src/pkg/regexp/regexp.go b/src/pkg/regexp/regexp.go
index 54c53776c..87e6b1c61 100644
--- a/src/pkg/regexp/regexp.go
+++ b/src/pkg/regexp/regexp.go
@@ -512,7 +512,7 @@ func (re *Regexp) replaceAll(bsrc []byte, src string, nmatch int, repl func(dst
}
// ReplaceAll returns a copy of src, replacing matches of the Regexp
-// with the replacement string repl. Inside repl, $ signs are interpreted as
+// with the replacement text repl. Inside repl, $ signs are interpreted as
// in Expand, so for instance $1 represents the text of the first submatch.
func (re *Regexp) ReplaceAll(src, repl []byte) []byte {
n := 2
@@ -726,7 +726,7 @@ func (re *Regexp) FindSubmatch(b []byte) [][]byte {
// the submatch with the corresponding index; other names refer to
// capturing parentheses named with the (?P<name>...) syntax. A
// reference to an out of range or unmatched index or a name that is not
-// present in the regular expression is replaced with an empty string.
+// present in the regular expression is replaced with an empty slice.
//
// In the $name form, name is taken to be as long as possible: $1x is
// equivalent to ${1x}, not ${1}x, and, $10 is equivalent to ${10}, not ${1}0.
diff --git a/src/pkg/regexp/syntax/parse.go b/src/pkg/regexp/syntax/parse.go
index 2df775025..4924e9453 100644
--- a/src/pkg/regexp/syntax/parse.go
+++ b/src/pkg/regexp/syntax/parse.go
@@ -48,6 +48,9 @@ const (
ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression"
)
+// TODO: Export for Go 1.1.
+const errUnexpectedParen ErrorCode = "unexpected )"
+
func (e ErrorCode) String() string {
return string(e)
}
@@ -1168,13 +1171,13 @@ func (p *parser) parseRightParen() error {
n := len(p.stack)
if n < 2 {
- return &Error{ErrInternalError, ""}
+ return &Error{errUnexpectedParen, p.wholeRegexp}
}
re1 := p.stack[n-1]
re2 := p.stack[n-2]
p.stack = p.stack[:n-2]
if re2.Op != opLeftParen {
- return &Error{ErrMissingParen, p.wholeRegexp}
+ return &Error{errUnexpectedParen, p.wholeRegexp}
}
// Restore flags at time of paren.
p.flags = re2.Flags
diff --git a/src/pkg/regexp/syntax/parse_test.go b/src/pkg/regexp/syntax/parse_test.go
index c6e63392c..81fd9dc01 100644
--- a/src/pkg/regexp/syntax/parse_test.go
+++ b/src/pkg/regexp/syntax/parse_test.go
@@ -441,10 +441,18 @@ var invalidRegexps = []string{
`(`,
`)`,
`(a`,
+ `a)`,
+ `(a))`,
`(a|b|`,
+ `a|b|)`,
+ `(a|b|))`,
`(a|b`,
+ `a|b)`,
+ `(a|b))`,
`[a-z`,
`([a-z)`,
+ `[a-z)`,
+ `([a-z]))`,
`x{1001}`,
`x{9876543210}`,
`x{2,1}`,
diff --git a/src/pkg/runtime/alg.c b/src/pkg/runtime/alg.c
index e3c42916e..36973eba3 100644
--- a/src/pkg/runtime/alg.c
+++ b/src/pkg/runtime/alg.c
@@ -5,6 +5,9 @@
#include "runtime.h"
#include "type.h"
+#define M0 (sizeof(uintptr)==4 ? 2860486313UL : 33054211828000289ULL)
+#define M1 (sizeof(uintptr)==4 ? 3267000013UL : 23344194077549503ULL)
+
/*
* map and chan helpers for
* dealing with unknown types
@@ -16,19 +19,13 @@ runtime·memhash(uintptr *h, uintptr s, void *a)
uintptr hash;
b = a;
- if(sizeof(hash) == 4)
- hash = 2860486313U;
- else
- hash = 33054211828000289ULL;
+ hash = M0;
while(s > 0) {
- if(sizeof(hash) == 4)
- hash = (hash ^ *b) * 3267000013UL;
- else
- hash = (hash ^ *b) * 23344194077549503ULL;
+ hash = (hash ^ *b) * M1;
b++;
s--;
}
- *h ^= hash;
+ *h = (*h ^ hash) * M1;
}
void
@@ -252,7 +249,7 @@ runtime·f32hash(uintptr *h, uintptr s, void *a)
hash = runtime·fastrand1(); // any kind of NaN
else
hash = *(uint32*)a;
- *h ^= (*h ^ hash ^ 2860486313U) * 3267000013U;
+ *h = (*h ^ hash ^ M0) * M1;
}
void
@@ -271,14 +268,11 @@ runtime·f64hash(uintptr *h, uintptr s, void *a)
else {
u = *(uint64*)a;
if(sizeof(uintptr) == 4)
- hash = ((uint32)(u>>32) * 3267000013UL) ^ (uint32)u;
+ hash = ((uint32)(u>>32) * M1) ^ (uint32)u;
else
hash = u;
}
- if(sizeof(uintptr) == 4)
- *h = (*h ^ hash ^ 2860486313U) * 3267000013U;
- else
- *h = (*h ^ hash ^ 33054211828000289ULL) * 23344194077549503ULL;
+ *h = (*h ^ hash ^ M0) * M1;
}
void
@@ -357,7 +351,7 @@ void
runtime·interhash(uintptr *h, uintptr s, void *a)
{
USED(s);
- *h ^= runtime·ifacehash(*(Iface*)a);
+ *h = (*h ^ runtime·ifacehash(*(Iface*)a)) * M1;
}
void
@@ -391,7 +385,7 @@ void
runtime·nilinterhash(uintptr *h, uintptr s, void *a)
{
USED(s);
- *h ^= runtime·efacehash(*(Eface*)a);
+ *h = (*h ^ runtime·efacehash(*(Eface*)a)) * M1;
}
void
diff --git a/src/pkg/runtime/extern.go b/src/pkg/runtime/extern.go
index f9c5b8e3d..d93259d7b 100644
--- a/src/pkg/runtime/extern.go
+++ b/src/pkg/runtime/extern.go
@@ -20,7 +20,7 @@ func Goexit()
// Caller reports file and line number information about function invocations on
// the calling goroutine's stack. The argument skip is the number of stack frames
-// to ascend, with 1 identifying the caller of Caller. (For historical reasons the
+// to ascend, with 0 identifying the caller of Caller. (For historical reasons the
// meaning of skip differs between Caller and Callers.) The return values report the
// program counter, file name, and line number within the file of the corresponding
// call. The boolean ok is false if it was not possible to recover the information.
@@ -28,7 +28,8 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool)
// Callers fills the slice pc with the program counters of function invocations
// on the calling goroutine's stack. The argument skip is the number of stack frames
-// to skip before recording in pc, with 0 starting at the caller of Callers.
+// to skip before recording in pc, with 0 identifying the frame for Callers itself and
+// 1 identifying the caller of Callers.
// It returns the number of entries written to pc.
func Callers(skip int, pc []uintptr) int
diff --git a/src/pkg/runtime/hashmap.c b/src/pkg/runtime/hashmap.c
index 1def96727..63ed4e2a3 100644
--- a/src/pkg/runtime/hashmap.c
+++ b/src/pkg/runtime/hashmap.c
@@ -6,17 +6,24 @@
#include "hashmap.h"
#include "type.h"
+/* Hmap flag values */
+#define IndirectVal (1<<0) /* storing pointers to values */
+#define IndirectKey (1<<1) /* storing pointers to keys */
+#define CanFreeTable (1<<2) /* okay to free subtables */
+#define CanFreeKey (1<<3) /* okay to free pointers to keys */
+
struct Hmap { /* a hash table; initialize with hash_init() */
uint32 count; /* elements in table - must be first */
uint8 datasize; /* amount of data to store in entry */
- uint8 max_power; /* max power of 2 to create sub-tables */
- uint8 indirectval; /* storing pointers to values */
+ uint8 flag;
uint8 valoff; /* offset of value in key+value data block */
int32 changes; /* inc'ed whenever a subtable is created/grown */
uintptr hash0; /* hash seed */
struct hash_subtable *st; /* first-level table */
};
+#define MaxData 255
+
struct hash_entry {
hash_hash_t hash; /* hash value of data */
byte data[1]; /* user data has "datasize" bytes */
@@ -54,6 +61,7 @@ struct hash_subtable {
((struct hash_entry *) (((byte *) (base)) + (byte_offset)))
#define HASH_MAX_PROBES 15 /* max entries to probe before rehashing */
+#define HASH_MAX_POWER 12 /* max power of 2 to create sub-tables */
/* return a hash layer with 2**power empty entries */
static struct hash_subtable *
@@ -82,7 +90,7 @@ hash_subtable_new (Hmap *h, int32 power, int32 used)
}
static void
-init_sizes (int64 hint, int32 *init_power, int32 *max_power)
+init_sizes (int64 hint, int32 *init_power)
{
int32 log = 0;
int32 i;
@@ -98,24 +106,20 @@ init_sizes (int64 hint, int32 *init_power, int32 *max_power)
} else {
*init_power = 12;
}
- *max_power = 12;
}
static void
hash_init (Hmap *h, int32 datasize, int64 hint)
{
int32 init_power;
- int32 max_power;
if(datasize < sizeof (void *))
datasize = sizeof (void *);
datasize = runtime·rnd(datasize, sizeof (void *));
- init_sizes (hint, &init_power, &max_power);
+ init_sizes (hint, &init_power);
h->datasize = datasize;
- h->max_power = max_power;
assert (h->datasize == datasize);
- assert (h->max_power == max_power);
- assert (sizeof (void *) <= h->datasize || h->max_power == 255);
+ assert (sizeof (void *) <= h->datasize);
h->count = 0;
h->changes = 0;
h->st = hash_subtable_new (h, init_power, 0);
@@ -253,7 +257,8 @@ hash_grow (MapType *t, Hmap *h, struct hash_subtable **pst, int32 flags)
used++;
}
}
- free (old_st);
+ if (h->flag & CanFreeTable)
+ free (old_st);
}
static int32
@@ -266,6 +271,7 @@ hash_lookup (MapType *t, Hmap *h, void *data, void **pres)
hash_hash_t e_hash;
struct hash_entry *e;
struct hash_entry *end_e;
+ void *key;
bool eq;
hash = h->hash0;
@@ -290,7 +296,10 @@ hash_lookup (MapType *t, Hmap *h, void *data, void **pres)
e = HASH_OFFSET (e, elemsize);
}
while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) {
- if (HASH_DATA_EQ (eq, t, h, data, e->data)) { /* a match */
+ key = e->data;
+ if (h->flag & IndirectKey)
+ key = *(void**)e->data;
+ if (HASH_DATA_EQ (eq, t, h, data, key)) { /* a match */
*pres = e->data;
return (1);
}
@@ -312,6 +321,7 @@ hash_remove (MapType *t, Hmap *h, void *data)
struct hash_entry *e;
struct hash_entry *end_e;
bool eq;
+ void *key;
hash = h->hash0;
(*t->key->alg->hash) (&hash, t->key->size, data);
@@ -335,8 +345,20 @@ hash_remove (MapType *t, Hmap *h, void *data)
e = HASH_OFFSET (e, elemsize);
}
while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) {
- if (HASH_DATA_EQ (eq, t, h, data, e->data)) { /* a match */
- if (h->indirectval)
+ key = e->data;
+ if (h->flag & IndirectKey)
+ key = *(void**)e->data;
+ if (HASH_DATA_EQ (eq, t, h, data, key)) { /* a match */
+ // Free key if indirect, but only if reflect can't be
+ // holding a pointer to it. Deletions are rare,
+ // indirect (large) keys are rare, reflect on maps
+ // is rare. So in the rare, rare, rare case of deleting
+ // an indirect key from a map that has been reflected on,
+ // we leave the key for garbage collection instead of
+ // freeing it here.
+ if (h->flag & CanFreeKey)
+ free (key);
+ if (h->flag & IndirectVal)
free (*(void**)((byte*)e->data + h->valoff));
hash_remove_n (st, e, 1);
h->count--;
@@ -385,8 +407,12 @@ hash_insert_internal (MapType *t, struct hash_subtable **pst, int32 flags, hash_
struct hash_entry *ins_e = e;
int32 ins_i = i;
hash_hash_t ins_e_hash;
+ void *key;
while (ins_e != end_e && ((e_hash = ins_e->hash) ^ hash) < HASH_SUBHASH) {
- if (HASH_DATA_EQ (eq, t, h, data, ins_e->data)) { /* a match */
+ key = ins_e->data;
+ if (h->flag & IndirectKey)
+ key = *(void**)key;
+ if (HASH_DATA_EQ (eq, t, h, data, key)) { /* a match */
*pres = ins_e->data;
return (1);
}
@@ -423,7 +449,7 @@ hash_insert_internal (MapType *t, struct hash_subtable **pst, int32 flags, hash_
return (0);
}
h->changes++;
- if (st->power < h->max_power) {
+ if (st->power < HASH_MAX_POWER) {
hash_grow (t, h, pst, flags);
} else {
hash_conv (t, h, st, flags, hash, start_e);
@@ -606,7 +632,7 @@ hash_iter_init (MapType *t, Hmap *h, struct hash_iter *it)
}
static void
-clean_st (struct hash_subtable *st, int32 *slots, int32 *used)
+clean_st (Hmap *h, struct hash_subtable *st, int32 *slots, int32 *used)
{
int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]);
struct hash_entry *e = st->entry;
@@ -617,13 +643,14 @@ clean_st (struct hash_subtable *st, int32 *slots, int32 *used)
while (e <= last) {
hash_hash_t hash = e->hash;
if ((hash & HASH_MASK) == HASH_SUBHASH) {
- clean_st (*(struct hash_subtable **)e->data, slots, used);
+ clean_st (h, *(struct hash_subtable **)e->data, slots, used);
} else {
lused += (hash != HASH_NIL);
}
e = HASH_OFFSET (e, elemsize);
}
- free (st);
+ if (h->flag & CanFreeTable)
+ free (st);
*slots += lslots;
*used += lused;
}
@@ -634,7 +661,7 @@ hash_destroy (Hmap *h)
int32 slots = 0;
int32 used = 0;
- clean_st (h->st, &slots, &used);
+ clean_st (h, h->st, &slots, &used);
free (h);
}
@@ -677,20 +704,23 @@ hash_visit (Hmap *h, void (*data_visit) (void *arg, int32 level, void *data), vo
/// interfaces to go runtime
//
-// hash requires < 256 bytes of data (key+value) stored inline.
-// Only basic types can be key - biggest is complex128 (16 bytes).
-// Leave some room to grow, just in case.
-enum {
- MaxValsize = 256 - 64
-};
+static void**
+hash_valptr(Hmap *h, void *p)
+{
+ p = (byte*)p + h->valoff;
+ if(h->flag & IndirectVal)
+ p = *(void**)p;
+ return p;
+}
+
static void**
-hash_indirect(Hmap *h, void *p)
+hash_keyptr(Hmap *h, void *p)
{
- if(h->indirectval)
+ if(h->flag & IndirectKey)
p = *(void**)p;
return p;
-}
+}
static int32 debug = 0;
@@ -699,8 +729,8 @@ Hmap*
runtime·makemap_c(MapType *typ, int64 hint)
{
Hmap *h;
- int32 valsize_in_hash;
Type *key, *val;
+ uintptr ksize, vsize;
key = typ->key;
val = typ->elem;
@@ -712,19 +742,29 @@ runtime·makemap_c(MapType *typ, int64 hint)
runtime·throw("runtime.makemap: unsupported map key type");
h = runtime·mal(sizeof(*h));
+ h->flag |= CanFreeTable; /* until reflect gets involved, free is okay */
+
+ ksize = runtime·rnd(key->size, sizeof(void*));
+ vsize = runtime·rnd(val->size, sizeof(void*));
+ if(ksize > MaxData || vsize > MaxData || ksize+vsize > MaxData) {
+ // Either key is too big, or value is, or combined they are.
+ // Prefer to keep the key if possible, because we look at
+ // keys more often than values.
+ if(ksize > MaxData - sizeof(void*)) {
+ // No choice but to indirect the key.
+ h->flag |= IndirectKey;
+ h->flag |= CanFreeKey; /* until reflect gets involved, free is okay */
+ ksize = sizeof(void*);
+ }
+ if(vsize > MaxData - ksize) {
+ // Have to indirect the value.
+ h->flag |= IndirectVal;
+ vsize = sizeof(void*);
+ }
+ }
- valsize_in_hash = val->size;
- if (val->size > MaxValsize) {
- h->indirectval = 1;
- valsize_in_hash = sizeof(void*);
- }
-
- // Align value inside data so that mark-sweep gc can find it.
- h->valoff = key->size;
- if(valsize_in_hash >= sizeof(void*))
- h->valoff = runtime·rnd(key->size, sizeof(void*));
-
- hash_init(h, h->valoff+valsize_in_hash, hint);
+ h->valoff = ksize;
+ hash_init(h, ksize+vsize, hint);
// these calculations are compiler dependent.
// figure out offsets of map call arguments.
@@ -773,7 +813,7 @@ runtime·mapaccess(MapType *t, Hmap *h, byte *ak, byte *av, bool *pres)
res = nil;
if(hash_lookup(t, h, ak, (void**)&res)) {
*pres = true;
- elem->alg->copy(elem->size, av, hash_indirect(h, res+h->valoff));
+ elem->alg->copy(elem->size, av, hash_valptr(h, res));
} else {
*pres = false;
elem->alg->copy(elem->size, av, nil);
@@ -877,10 +917,14 @@ runtime·mapassign(MapType *t, Hmap *h, byte *ak, byte *av)
res = nil;
hit = hash_insert(t, h, ak, (void**)&res);
- if(!hit && h->indirectval)
- *(void**)(res+h->valoff) = runtime·mal(t->elem->size);
- t->key->alg->copy(t->key->size, res, ak);
- t->elem->alg->copy(t->elem->size, hash_indirect(h, res+h->valoff), av);
+ if(!hit) {
+ if(h->flag & IndirectKey)
+ *(void**)res = runtime·mal(t->key->size);
+ if(h->flag & IndirectVal)
+ *(void**)(res+h->valoff) = runtime·mal(t->elem->size);
+ }
+ t->key->alg->copy(t->key->size, hash_keyptr(h, res), ak);
+ t->elem->alg->copy(t->elem->size, hash_valptr(h, res), av);
if(debug) {
runtime·prints("mapassign: map=");
@@ -985,6 +1029,22 @@ runtime·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it)
void
reflect·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it)
{
+ uint8 flag;
+
+ if(h != nil && t->key->size > sizeof(void*)) {
+ // reflect·mapiterkey returns pointers to key data,
+ // and reflect holds them, so we cannot free key data
+ // eagerly anymore. Updating h->flag now is racy,
+ // but it's okay because this is the only possible store
+ // after creation.
+ flag = h->flag;
+ if(flag & IndirectKey)
+ flag &= ~CanFreeKey;
+ else
+ flag &= ~CanFreeTable;
+ h->flag = flag;
+ }
+
it = runtime·mal(sizeof *it);
FLUSH(&it);
runtime·mapiterinit(t, h, it);
@@ -1032,7 +1092,7 @@ runtime·mapiter1(struct hash_iter *it, ...)
runtime·throw("runtime.mapiter1: key:val nil pointer");
key = it->t->key;
- key->alg->copy(key->size, ak, res);
+ key->alg->copy(key->size, ak, hash_keyptr(h, res));
if(debug) {
runtime·prints("mapiter2: iter=");
@@ -1053,7 +1113,7 @@ runtime·mapiterkey(struct hash_iter *it, void *ak)
if(res == nil)
return false;
key = it->t->key;
- key->alg->copy(key->size, ak, res);
+ key->alg->copy(key->size, ak, hash_keyptr(it->h, res));
return true;
}
@@ -1076,6 +1136,7 @@ reflect·mapiterkey(struct hash_iter *it, uintptr key, bool ok)
} else {
tkey = it->t->key;
key = 0;
+ res = (byte*)hash_keyptr(it->h, res);
if(tkey->size <= sizeof(key))
tkey->alg->copy(tkey->size, (byte*)&key, res);
else
@@ -1117,8 +1178,8 @@ runtime·mapiter2(struct hash_iter *it, ...)
runtime·throw("runtime.mapiter2: key:val nil pointer");
h = it->h;
- t->key->alg->copy(t->key->size, ak, res);
- t->elem->alg->copy(t->elem->size, av, hash_indirect(h, res+h->valoff));
+ t->key->alg->copy(t->key->size, ak, hash_keyptr(h, res));
+ t->elem->alg->copy(t->elem->size, av, hash_valptr(h, res));
if(debug) {
runtime·prints("mapiter2: iter=");
diff --git a/src/pkg/runtime/malloc.goc b/src/pkg/runtime/malloc.goc
index fbdd6bb02..9ae3a9d61 100644
--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -154,6 +154,7 @@ runtime·free(void *v)
c->local_by_size[sizeclass].nfree++;
runtime·MCache_Free(c, v, sizeclass, size);
}
+ c->local_nfree++;
c->local_alloc -= size;
if(prof)
runtime·MProf_Free(v, size);
diff --git a/src/pkg/runtime/mfinal.c b/src/pkg/runtime/mfinal.c
index c6f2b5421..1fa5ea401 100644
--- a/src/pkg/runtime/mfinal.c
+++ b/src/pkg/runtime/mfinal.c
@@ -150,8 +150,7 @@ runtime·addfinalizer(void *p, void (*f)(void*), int32 nret)
tab = TAB(p);
runtime·lock(tab);
if(f == nil) {
- if(lookfintab(tab, p, true, nil))
- runtime·setblockspecial(p, false);
+ lookfintab(tab, p, true, nil);
runtime·unlock(tab);
return true;
}
diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c
index e043864c1..e8fb266f4 100644
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -1066,7 +1066,6 @@ runfinq(void)
framecap = framesz;
}
*(void**)frame = f->arg;
- runtime·setblockspecial(f->arg, false);
reflect·call((byte*)f->fn, frame, sizeof(uintptr) + f->nret);
f->fn = nil;
f->arg = nil;
diff --git a/src/pkg/runtime/softfloat_arm.c b/src/pkg/runtime/softfloat_arm.c
index fbe0b0413..bd73cb15b 100644
--- a/src/pkg/runtime/softfloat_arm.c
+++ b/src/pkg/runtime/softfloat_arm.c
@@ -9,10 +9,10 @@
#include "runtime.h"
#define CPSR 14
-#define FLAGS_N (1 << 31)
-#define FLAGS_Z (1 << 30)
-#define FLAGS_C (1 << 29)
-#define FLAGS_V (1 << 28)
+#define FLAGS_N (1U << 31)
+#define FLAGS_Z (1U << 30)
+#define FLAGS_C (1U << 29)
+#define FLAGS_V (1U << 28)
void runtime·abort(void);
void math·sqrtC(uint64, uint64*);
@@ -86,12 +86,24 @@ fstatus(bool nan, int32 cmp)
return FLAGS_C;
}
+// conditions array record the required CPSR cond field for the
+// first 5 pairs of conditional execution opcodes
+// higher 4 bits are must set, lower 4 bits are must clear
+static const uint8 conditions[10/2] = {
+ [0/2] = (FLAGS_Z >> 24) | 0, // 0: EQ (Z set), 1: NE (Z clear)
+ [2/2] = (FLAGS_C >> 24) | 0, // 2: CS/HS (C set), 3: CC/LO (C clear)
+ [4/2] = (FLAGS_N >> 24) | 0, // 4: MI (N set), 5: PL (N clear)
+ [6/2] = (FLAGS_V >> 24) | 0, // 6: VS (V set), 7: VC (V clear)
+ [8/2] = (FLAGS_C >> 24) |
+ (FLAGS_Z >> 28), // 8: HI (C set and Z clear), 9: LS (C clear and Z set)
+};
+
// returns number of words that the fp instruction
// is occupying, 0 if next instruction isn't float.
static uint32
stepflt(uint32 *pc, uint32 *regs)
{
- uint32 i, regd, regm, regn;
+ uint32 i, opc, regd, regm, regn, cpsr;
int32 delta;
uint32 *addr;
uint64 uval;
@@ -102,8 +114,49 @@ stepflt(uint32 *pc, uint32 *regs)
i = *pc;
if(trace)
- runtime·printf("stepflt %p %x\n", pc, i);
+ runtime·printf("stepflt %p %x (cpsr %x)\n", pc, i, regs[CPSR] >> 28);
+
+ opc = i >> 28;
+ if(opc == 14) // common case first
+ goto execute;
+ cpsr = regs[CPSR] >> 28;
+ switch(opc) {
+ case 0: case 1: case 2: case 3: case 4:
+ case 5: case 6: case 7: case 8: case 9:
+ if(((cpsr & (conditions[opc/2] >> 4)) == (conditions[opc/2] >> 4)) &&
+ ((cpsr & (conditions[opc/2] & 0xf)) == 0)) {
+ if(opc & 1) return 1;
+ } else {
+ if(!(opc & 1)) return 1;
+ }
+ break;
+ case 10: // GE (N == V)
+ case 11: // LT (N != V)
+ if((cpsr & (FLAGS_N >> 28)) == (cpsr & (FLAGS_V >> 28))) {
+ if(opc & 1) return 1;
+ } else {
+ if(!(opc & 1)) return 1;
+ }
+ break;
+ case 12: // GT (N == V and Z == 0)
+ case 13: // LE (N != V or Z == 1)
+ if((cpsr & (FLAGS_N >> 28)) == (cpsr & (FLAGS_V >> 28)) &&
+ (cpsr & (FLAGS_Z >> 28)) == 0) {
+ if(opc & 1) return 1;
+ } else {
+ if(!(opc & 1)) return 1;
+ }
+ break;
+ case 14: // AL
+ break;
+ case 15: // shouldn't happen
+ return 0;
+ }
+ if(trace)
+ runtime·printf("conditional %x (cpsr %x) pass\n", opc, cpsr);
+ i = (0xeU << 28) | (i & 0xfffffff);
+execute:
// special cases
if((i&0xfffff000) == 0xe59fb000) {
// load r11 from pc-relative address.
diff --git a/src/pkg/runtime/time.goc b/src/pkg/runtime/time.goc
index a6b835247..b18902f00 100644
--- a/src/pkg/runtime/time.goc
+++ b/src/pkg/runtime/time.goc
@@ -61,8 +61,11 @@ runtime·tsleep(int64 ns)
{
Timer t;
- if(ns <= 0)
+ if(ns <= 0) {
+ g->status = Grunning;
+ g->waitreason = nil;
return;
+ }
t.when = runtime·nanotime() + ns;
t.period = 0;
diff --git a/src/pkg/runtime/vlrt_arm.c b/src/pkg/runtime/vlrt_arm.c
index 50f33710b..ab8050177 100644
--- a/src/pkg/runtime/vlrt_arm.c
+++ b/src/pkg/runtime/vlrt_arm.c
@@ -197,12 +197,14 @@ void
runtime·int64tofloat64(Vlong y, double d)
{
d = _v2d(y);
+ USED(&d); // FLUSH
}
void
runtime·uint64tofloat64(Vlong y, double d)
{
d = _ul2d(y.hi)*4294967296. + _ul2d(y.lo);
+ USED(&d); // FLUSH
}
static void
diff --git a/src/pkg/strconv/itoa.go b/src/pkg/strconv/itoa.go
index ca40dd7ef..67f17d866 100644
--- a/src/pkg/strconv/itoa.go
+++ b/src/pkg/strconv/itoa.go
@@ -4,13 +4,17 @@
package strconv
-// FormatUint returns the string representation of i in the given base.
+// FormatUint returns the string representation of i in the given base,
+// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
+// for digit values >= 10.
func FormatUint(i uint64, base int) string {
_, s := formatBits(nil, i, base, false, false)
return s
}
-// FormatInt returns the string representation of i in the given base.
+// FormatInt returns the string representation of i in the given base,
+// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
+// for digit values >= 10.
func FormatInt(i int64, base int) string {
_, s := formatBits(nil, uint64(i), base, i < 0, false)
return s
diff --git a/src/pkg/strings/example_test.go b/src/pkg/strings/example_test.go
index 114171072..733caf5f2 100644
--- a/src/pkg/strings/example_test.go
+++ b/src/pkg/strings/example_test.go
@@ -41,7 +41,6 @@ func ExampleContainsAny() {
func ExampleCount() {
fmt.Println(strings.Count("cheese", "e"))
fmt.Println(strings.Count("five", "")) // before & after each rune
-
// Output:
// 3
// 5
diff --git a/src/pkg/syscall/syscall_linux_arm.go b/src/pkg/syscall/syscall_linux_arm.go
index 48b5d31d7..2f2907945 100644
--- a/src/pkg/syscall/syscall_linux_arm.go
+++ b/src/pkg/syscall/syscall_linux_arm.go
@@ -41,26 +41,28 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error)
//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error)
//sys sendmsg(s int, msg *Msghdr, flags int) (err error)
-//sys Chown(path string, uid int, gid int) (err error)
-//sys Fchown(fd int, uid int, gid int) (err error)
+// 64-bit file system and 32-bit uid calls
+// (16-bit uid calls are not always supported in newer kernels)
+//sys Chown(path string, uid int, gid int) (err error) = SYS_CHOWN32
+//sys Fchown(fd int, uid int, gid int) (err error) = SYS_FCHOWN32
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
//sys Fstatfs(fd int, buf *Statfs_t) (err error) = SYS_FSTATFS64
-//sysnb Getegid() (egid int)
-//sysnb Geteuid() (euid int)
-//sysnb Getgid() (gid int)
-//sysnb Getuid() (uid int)
-//sys Lchown(path string, uid int, gid int) (err error)
+//sysnb Getegid() (egid int) = SYS_GETEGID32
+//sysnb Geteuid() (euid int) = SYS_GETEUID32
+//sysnb Getgid() (gid int) = SYS_GETGID32
+//sysnb Getuid() (uid int) = SYS_GETUID32
+//sys Lchown(path string, uid int, gid int) (err error) = SYS_LCHOWN32
//sys Listen(s int, n int) (err error)
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64
//sys Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT
-//sys Setfsgid(gid int) (err error)
-//sys Setfsuid(uid int) (err error)
-//sysnb Setgid(gid int) (err error)
-//sysnb Setregid(rgid int, egid int) (err error)
-//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
-//sysnb Setresuid(ruid int, euid int, suid int) (err error)
-//sysnb Setreuid(ruid int, euid int) (err error)
+//sys Setfsgid(gid int) (err error) = SYS_SETFSGID32
+//sys Setfsuid(uid int) (err error) = SYS_SETFSUID32
+//sysnb Setgid(gid int) (err error) = SYS_SETGID32
+//sysnb Setregid(rgid int, egid int) (err error) = SYS_SETREGID32
+//sysnb Setresgid(rgid int, egid int, sgid int) (err error) = SYS_SETRESGID32
+//sysnb Setresuid(ruid int, euid int, suid int) (err error) = SYS_SETRESUID32
+//sysnb Setreuid(ruid int, euid int) (err error) = SYS_SETREUID32
//sys Shutdown(fd int, how int) (err error)
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error)
//sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64
diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go
index 47209da8f..6b544f1d4 100644
--- a/src/pkg/syscall/syscall_windows.go
+++ b/src/pkg/syscall/syscall_windows.go
@@ -90,7 +90,9 @@ func (e Errno) Error() string {
b := make([]uint16, 300)
n, err := FormatMessage(flags, 0, uint32(e), langid(LANG_ENGLISH, SUBLANG_ENGLISH_US), b, nil)
if err != nil {
- return "error " + itoa(int(e)) + " (FormatMessage failed with err=" + itoa(int(err.(Errno))) + ")"
+ // TODO(brainman): Call FormatMessage again asking for "native" error message.
+ // http://code.google.com/p/go/issues/detail?id=3376 must be resolved first.
+ return "winapi error #" + itoa(int(e))
}
// trim terminating \r and \n
for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- {
@@ -127,8 +129,8 @@ func NewCallback(fn interface{}) uintptr
//sys SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, err error) [failretval==0xffffffff]
//sys CloseHandle(handle Handle) (err error)
//sys GetStdHandle(stdhandle int) (handle Handle, err error) [failretval==InvalidHandle]
-//sys FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) [failretval==InvalidHandle] = FindFirstFileW
-//sys FindNextFile(handle Handle, data *Win32finddata) (err error) = FindNextFileW
+//sys findFirstFile1(name *uint16, data *win32finddata1) (handle Handle, err error) [failretval==InvalidHandle] = FindFirstFileW
+//sys findNextFile1(handle Handle, data *win32finddata1) (err error) = FindNextFileW
//sys FindClose(handle Handle) (err error)
//sys GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (err error)
//sys GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, err error) = GetCurrentDirectoryW
@@ -199,6 +201,7 @@ func NewCallback(fn interface{}) uintptr
//sys RegQueryInfoKey(key Handle, class *uint16, classLen *uint32, reserved *uint32, subkeysLen *uint32, maxSubkeyLen *uint32, maxClassLen *uint32, valuesLen *uint32, maxValueNameLen *uint32, maxValueLen *uint32, saLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegQueryInfoKeyW
//sys RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegEnumKeyExW
//sys RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegQueryValueExW
+//sys getCurrentProcessId() (pid uint32) = kernel32.getCurrentProcessId
// syscall interface implementation for other packages
@@ -681,9 +684,35 @@ func SetsockoptIPMreq(fd Handle, level, opt int, mreq *IPMreq) (err error) {
}
func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (err error) { return EWINDOWS }
-// TODO(brainman): fix all needed for os
+func Getpid() (pid int) { return int(getCurrentProcessId()) }
+
+func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) {
+ // NOTE(rsc): The Win32finddata struct is wrong for the system call:
+ // the two paths are each one uint16 short. Use the correct struct,
+ // a win32finddata1, and then copy the results out.
+ // There is no loss of expressivity here, because the final
+ // uint16, if it is used, is supposed to be a NUL, and Go doesn't need that.
+ // For Go 1.1, we might avoid the allocation of win32finddata1 here
+ // by adding a final Bug [2]uint16 field to the struct and then
+ // adjusting the fields in the result directly.
+ var data1 win32finddata1
+ handle, err = findFirstFile1(name, &data1)
+ if err == nil {
+ copyFindData(data, &data1)
+ }
+ return
+}
-func Getpid() (pid int) { return -1 }
+func FindNextFile(handle Handle, data *Win32finddata) (err error) {
+ var data1 win32finddata1
+ err = findNextFile1(handle, &data1)
+ if err == nil {
+ copyFindData(data, &data1)
+ }
+ return
+}
+
+// TODO(brainman): fix all needed for os
func Getppid() (ppid int) { return -1 }
func Fchdir(fd Handle) (err error) { return EWINDOWS }
diff --git a/src/pkg/syscall/syscall_windows_test.go b/src/pkg/syscall/syscall_windows_test.go
new file mode 100644
index 000000000..79cd8f869
--- /dev/null
+++ b/src/pkg/syscall/syscall_windows_test.go
@@ -0,0 +1,50 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syscall_test
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "syscall"
+ "testing"
+)
+
+func TestWin32finddata(t *testing.T) {
+ dir, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatalf("failed to create temp directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ path := filepath.Join(dir, "long_name.and_extension")
+ f, err := os.Create(path)
+ if err != nil {
+ t.Fatalf("failed to create %v: %v", path, err)
+ }
+ f.Close()
+
+ type X struct {
+ fd syscall.Win32finddata
+ got byte
+ pad [10]byte // to protect ourselves
+
+ }
+ var want byte = 2 // it is unlikely to have this character in the filename
+ x := X{got: want}
+
+ h, err := syscall.FindFirstFile(syscall.StringToUTF16Ptr(path), &(x.fd))
+ if err != nil {
+ t.Fatalf("FindFirstFile failed: %v", err)
+ }
+ err = syscall.FindClose(h)
+ if err != nil {
+ t.Fatalf("FindClose failed: %v", err)
+ }
+
+ if x.got != want {
+ t.Fatalf("memory corruption: want=%d got=%d", want, x.got)
+ }
+}
diff --git a/src/pkg/syscall/zsyscall_linux_arm.go b/src/pkg/syscall/zsyscall_linux_arm.go
index 1e86d3b7f..b1a59a69e 100644
--- a/src/pkg/syscall/zsyscall_linux_arm.go
+++ b/src/pkg/syscall/zsyscall_linux_arm.go
@@ -1203,7 +1203,7 @@ func sendmsg(s int, msg *Msghdr, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ _, _, e1 := Syscall(SYS_CHOWN32, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -1213,7 +1213,7 @@ func Chown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchown(fd int, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
+ _, _, e1 := Syscall(SYS_FCHOWN32, uintptr(fd), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -1243,7 +1243,7 @@ func Fstatfs(fd int, buf *Statfs_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getegid() (egid int) {
- r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEGID32, 0, 0, 0)
egid = int(r0)
return
}
@@ -1251,7 +1251,7 @@ func Getegid() (egid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Geteuid() (euid int) {
- r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEUID32, 0, 0, 0)
euid = int(r0)
return
}
@@ -1259,7 +1259,7 @@ func Geteuid() (euid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getgid() (gid int) {
- r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETGID32, 0, 0, 0)
gid = int(r0)
return
}
@@ -1267,7 +1267,7 @@ func Getgid() (gid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getuid() (uid int) {
- r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETUID32, 0, 0, 0)
uid = int(r0)
return
}
@@ -1275,7 +1275,7 @@ func Getuid() (uid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lchown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ _, _, e1 := Syscall(SYS_LCHOWN32, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -1327,7 +1327,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setfsgid(gid int) (err error) {
- _, _, e1 := Syscall(SYS_SETFSGID, uintptr(gid), 0, 0)
+ _, _, e1 := Syscall(SYS_SETFSGID32, uintptr(gid), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1337,7 +1337,7 @@ func Setfsgid(gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setfsuid(uid int) (err error) {
- _, _, e1 := Syscall(SYS_SETFSUID, uintptr(uid), 0, 0)
+ _, _, e1 := Syscall(SYS_SETFSUID32, uintptr(uid), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1347,7 +1347,7 @@ func Setfsuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setgid(gid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETGID32, uintptr(gid), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1357,7 +1357,7 @@ func Setgid(gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setregid(rgid int, egid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREGID32, uintptr(rgid), uintptr(egid), 0)
if e1 != 0 {
err = e1
}
@@ -1367,7 +1367,7 @@ func Setregid(rgid int, egid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setresgid(rgid int, egid int, sgid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
+ _, _, e1 := RawSyscall(SYS_SETRESGID32, uintptr(rgid), uintptr(egid), uintptr(sgid))
if e1 != 0 {
err = e1
}
@@ -1377,7 +1377,7 @@ func Setresgid(rgid int, egid int, sgid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setresuid(ruid int, euid int, suid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
+ _, _, e1 := RawSyscall(SYS_SETRESUID32, uintptr(ruid), uintptr(euid), uintptr(suid))
if e1 != 0 {
err = e1
}
@@ -1387,7 +1387,7 @@ func Setresuid(ruid int, euid int, suid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setreuid(ruid int, euid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREUID32, uintptr(ruid), uintptr(euid), 0)
if e1 != 0 {
err = e1
}
diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go
index 8e6afcf0a..3022ce2e9 100644
--- a/src/pkg/syscall/zsyscall_windows_386.go
+++ b/src/pkg/syscall/zsyscall_windows_386.go
@@ -142,6 +142,7 @@ var (
procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken")
procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation")
procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
+ procgetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
)
func GetLastError() (lasterr error) {
@@ -308,7 +309,7 @@ func GetStdHandle(stdhandle int) (handle Handle, err error) {
return
}
-func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) {
+func findFirstFile1(name *uint16, data *win32finddata1) (handle Handle, err error) {
r0, _, e1 := Syscall(procFindFirstFileW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(data)), 0)
handle = Handle(r0)
if handle == InvalidHandle {
@@ -321,7 +322,7 @@ func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error)
return
}
-func FindNextFile(handle Handle, data *Win32finddata) (err error) {
+func findNextFile1(handle Handle, data *win32finddata1) (err error) {
r1, _, e1 := Syscall(procFindNextFileW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -1600,3 +1601,9 @@ func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) {
}
return
}
+
+func getCurrentProcessId() (pid uint32) {
+ r0, _, _ := Syscall(procgetCurrentProcessId.Addr(), 0, 0, 0, 0)
+ pid = uint32(r0)
+ return
+}
diff --git a/src/pkg/syscall/zsyscall_windows_amd64.go b/src/pkg/syscall/zsyscall_windows_amd64.go
index c6ff6fe02..ea0000c32 100644
--- a/src/pkg/syscall/zsyscall_windows_amd64.go
+++ b/src/pkg/syscall/zsyscall_windows_amd64.go
@@ -142,6 +142,7 @@ var (
procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken")
procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation")
procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
+ procgetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
)
func GetLastError() (lasterr error) {
@@ -308,7 +309,7 @@ func GetStdHandle(stdhandle int) (handle Handle, err error) {
return
}
-func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) {
+func findFirstFile1(name *uint16, data *win32finddata1) (handle Handle, err error) {
r0, _, e1 := Syscall(procFindFirstFileW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(data)), 0)
handle = Handle(r0)
if handle == InvalidHandle {
@@ -321,7 +322,7 @@ func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error)
return
}
-func FindNextFile(handle Handle, data *Win32finddata) (err error) {
+func findNextFile1(handle Handle, data *win32finddata1) (err error) {
r1, _, e1 := Syscall(procFindNextFileW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -1600,3 +1601,9 @@ func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) {
}
return
}
+
+func getCurrentProcessId() (pid uint32) {
+ r0, _, _ := Syscall(procgetCurrentProcessId.Addr(), 0, 0, 0, 0)
+ pid = uint32(r0)
+ return
+}
diff --git a/src/pkg/syscall/ztypes_windows.go b/src/pkg/syscall/ztypes_windows.go
index 54168bb98..b2dc12e1a 100644
--- a/src/pkg/syscall/ztypes_windows.go
+++ b/src/pkg/syscall/ztypes_windows.go
@@ -347,6 +347,38 @@ type Win32finddata struct {
AlternateFileName [13]uint16
}
+// This is the actual system call structure.
+// Win32finddata is what we committed to in Go 1.
+type win32finddata1 struct {
+ FileAttributes uint32
+ CreationTime Filetime
+ LastAccessTime Filetime
+ LastWriteTime Filetime
+ FileSizeHigh uint32
+ FileSizeLow uint32
+ Reserved0 uint32
+ Reserved1 uint32
+ FileName [MAX_PATH]uint16
+ AlternateFileName [14]uint16
+}
+
+func copyFindData(dst *Win32finddata, src *win32finddata1) {
+ dst.FileAttributes = src.FileAttributes
+ dst.CreationTime = src.CreationTime
+ dst.LastAccessTime = src.LastAccessTime
+ dst.LastWriteTime = src.LastWriteTime
+ dst.FileSizeHigh = src.FileSizeHigh
+ dst.FileSizeLow = src.FileSizeLow
+ dst.Reserved0 = src.Reserved0
+ dst.Reserved1 = src.Reserved1
+
+ // The src is 1 element shorter than dst. Zero that last one.
+ copy(dst.FileName[:], src.FileName[:])
+ dst.FileName[len(dst.FileName)-1] = 0
+ copy(dst.AlternateFileName[:], src.AlternateFileName[:])
+ src.AlternateFileName[len(dst.AlternateFileName)-1] = 0
+}
+
type ByHandleFileInformation struct {
FileAttributes uint32
CreationTime Filetime
diff --git a/src/pkg/text/template/exec.go b/src/pkg/text/template/exec.go
index feb434a3b..aba21ce28 100644
--- a/src/pkg/text/template/exec.go
+++ b/src/pkg/text/template/exec.go
@@ -518,6 +518,13 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu
}
}
if !value.Type().AssignableTo(typ) {
+ if value.Kind() == reflect.Interface && !value.IsNil() {
+ value = value.Elem()
+ if value.Type().AssignableTo(typ) {
+ return value
+ }
+ // fallthrough
+ }
// Does one dereference or indirection work? We could do more, as we
// do with method receivers, but that gets messy and method receivers
// are much more constrained, so it makes more sense there than here.
diff --git a/src/pkg/text/template/exec_test.go b/src/pkg/text/template/exec_test.go
index 37d25f470..f4ae50f0e 100644
--- a/src/pkg/text/template/exec_test.go
+++ b/src/pkg/text/template/exec_test.go
@@ -311,6 +311,7 @@ var execTests = []execTest{
{".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=<he+llo>", tVal, true},
{"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true},
{"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true},
+ {"Interface Call", `{{stringer .S}}`, "foozle", map[string]interface{}{"S": bytes.NewBufferString("foozle")}, true},
// Erroneous function calls (check args).
{".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false},
@@ -507,6 +508,10 @@ func vfunc(V, *V) string {
return "vfunc"
}
+func stringer(s fmt.Stringer) string {
+ return s.String()
+}
+
func testExecute(execTests []execTest, template *Template, t *testing.T) {
b := new(bytes.Buffer)
funcs := FuncMap{
@@ -516,6 +521,7 @@ func testExecute(execTests []execTest, template *Template, t *testing.T) {
"typeOf": typeOf,
"vfunc": vfunc,
"zeroArgs": zeroArgs,
+ "stringer": stringer,
}
for _, test := range execTests {
var tmpl *Template
diff --git a/src/pkg/time/sleep_test.go b/src/pkg/time/sleep_test.go
index 526d58d75..e05773df6 100644
--- a/src/pkg/time/sleep_test.go
+++ b/src/pkg/time/sleep_test.go
@@ -223,3 +223,25 @@ func TestTimerStopStress(t *testing.T) {
}
Sleep(3 * Second)
}
+
+func TestSleepZeroDeadlock(t *testing.T) {
+ // Sleep(0) used to hang, the sequence of events was as follows.
+ // Sleep(0) sets G's status to Gwaiting, but then immediately returns leaving the status.
+ // Then the goroutine calls e.g. new and falls down into the scheduler due to pending GC.
+ // After the GC nobody wakes up the goroutine from Gwaiting status.
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
+ c := make(chan bool)
+ go func() {
+ for i := 0; i < 100; i++ {
+ runtime.GC()
+ }
+ c <- true
+ }()
+ for i := 0; i < 100; i++ {
+ Sleep(0)
+ tmp := make(chan bool, 1)
+ tmp <- true
+ <-tmp
+ }
+ <-c
+}
diff --git a/src/pkg/unicode/maketables.go b/src/pkg/unicode/maketables.go
index 16bc83cea..fcd14fc73 100644
--- a/src/pkg/unicode/maketables.go
+++ b/src/pkg/unicode/maketables.go
@@ -488,7 +488,7 @@ func printCategories() {
func(code rune) bool { return chars[code].category == name })
}
decl.Sort()
- fmt.Println("// The following variables are of type *RangeTable:")
+ fmt.Println("// These variables have type *RangeTable.")
fmt.Println("var (")
for _, d := range decl {
fmt.Print(d)
@@ -771,7 +771,7 @@ func printScriptOrProperty(doProps bool) {
fmt.Print("}\n\n")
}
decl.Sort()
- fmt.Println("// The following variables are of type *RangeTable:")
+ fmt.Println("// These variables have type *RangeTable.")
fmt.Println("var (")
for _, d := range decl {
fmt.Print(d)
diff --git a/src/pkg/unicode/tables.go b/src/pkg/unicode/tables.go
index 5009e6b98..ebd169b09 100644
--- a/src/pkg/unicode/tables.go
+++ b/src/pkg/unicode/tables.go
@@ -2701,7 +2701,7 @@ var _Zs = &RangeTable{
},
}
-// The following variables are of type *RangeTable:
+// These variables have type *RangeTable.
var (
Cc = _Cc // Cc is the set of Unicode characters in category Cc.
Cf = _Cf // Cf is the set of Unicode characters in category Cf.
@@ -4054,7 +4054,7 @@ var _Yi = &RangeTable{
},
}
-// The following variables are of type *RangeTable:
+// These variables have type *RangeTable.
var (
Arabic = _Arabic // Arabic is the set of Unicode characters in script Arabic.
Armenian = _Armenian // Armenian is the set of Unicode characters in script Armenian.
@@ -5116,7 +5116,7 @@ var _White_Space = &RangeTable{
},
}
-// The following variables are of type *RangeTable:
+// These variables have type *RangeTable.
var (
ASCII_Hex_Digit = _ASCII_Hex_Digit // ASCII_Hex_Digit is the set of Unicode characters with property ASCII_Hex_Digit.
Bidi_Control = _Bidi_Control // Bidi_Control is the set of Unicode characters with property Bidi_Control.
diff --git a/src/run.bash b/src/run.bash
index c2a52a078..ca84b7034 100755
--- a/src/run.bash
+++ b/src/run.bash
@@ -24,6 +24,11 @@ else
echo
fi
+# we must unset GOROOT_FINAL before tests, because runtime/debug requires
+# correct access to source code, so if we have GOROOT_FINAL in effect,
+# at least runtime/debug test will fail.
+unset GOROOT_FINAL
+
echo '# Testing packages.'
time go test std -short -timeout=120s
echo
@@ -101,7 +106,7 @@ time go run run.go
echo
echo '# Checking API compatibility.'
-go tool api -c $GOROOT/api/go1.txt
+go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt
echo
echo ALL TESTS PASSED
diff --git a/src/run.bat b/src/run.bat
index c7a157972..9a09d435c 100644
--- a/src/run.bat
+++ b/src/run.bat
@@ -25,6 +25,11 @@ if errorlevel 1 goto fail
echo.
:norebuild
+:: we must unset GOROOT_FINAL before tests, because runtime/debug requires
+:: correct access to source code, so if we have GOROOT_FINAL in effect,
+:: at least runtime/debug test will fail.
+set GOROOT_FINAL=
+
echo # Testing packages.
go test std -short -timeout=120s
if errorlevel 1 goto fail
@@ -63,7 +68,7 @@ echo.
if %FAIL%==1 goto fail
echo # Checking API compatibility.
-go tool api -c ..\api\go1.txt
+go tool api -c ..\api\go1.txt -next ..\api\next.txt
if errorlevel 1 goto fail
echo.