diff options
Diffstat (limited to 'src')
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. | 
