--- /dev/null
+Please answer these questions before submitting your issue. Thanks!
+
+1. What version of Go are you using (`go version`)?
+
+
+2. What operating system and processor architecture are you using (`go env`)?
+
+
+3. What did you do?
+(Use play.golang.org to provide a runnable example, if possible.)
+
+
+4. What did you expect to see?
+
+
+5. What did you see instead?
+
+
--- /dev/null
+Please do not send pull requests to the golang/* repositories.
+
+We do, however, take contributions gladly.
+
+See https://golang.org/doc/contribute.html
+
+Thanks!
src/cmd/cgo/zdefaultcc.go
src/cmd/go/zdefaultcc.go
src/cmd/internal/obj/zbootstrap.go
+src/go/build/zcgo.go
src/go/doc/headscan
src/runtime/internal/sys/zversion.go
src/unicode/maketables
Tools:
+cmd/dist: add list subcommand to list all supported platforms (CL 19837)
cmd/go: GO15VENDOREXPERIMENT gone, assumed on (CL 19615)
cmd/link: "-X name value" form gone (CL 19614)
+cmd/compile: smaller binaries (many CLs)
+cmd/go, go/build: add support for Fortran (CL 19670, CL 4114)
+cmd/dist: introduce list subcommand to list all supported platforms (CL 19837)
Ports:
-SOMETHING WILL HAPPEN
+We now require OpenBSD 5.6+ (CL 18219, crypto/rand using getentropy)
+plan9/arm support? Start at least.
API additions and behavior changes:
-SOMETHING WILL HAPPEN
-
-
+runtime: add CallerFrames and Frames (CL 19869)
+testing/quick: now generates nil values (CL 16470)
+net/url: support query string without values (CL 19931)
+net/textproto: permit all valid token chars in CanonicalMIMEHeaderKey input (CL 18725)
<th width="50"></th><th align="left" width="100"><code>$GOOS</code></th> <th align="left" width="100"><code>$GOARCH</code></th>
</tr>
<tr>
+<td></td><td><code>android</code></td> <td><code>arm</code></td>
+</tr>
+<tr>
<td></td><td><code>darwin</code></td> <td><code>386</code></td>
</tr>
<tr>
body: `i := 0; p := &S{p:&i}; s := p.a[:]; C.f(unsafe.Pointer(&s[0]))`,
fail: false,
},
+ {
+ // Passing the address of a slice of an array that is
+ // an element in a struct, with a type conversion.
+ name: "slice-ok-4",
+ c: `typedef void* PV; void f(PV p) {}`,
+ imports: []string{"unsafe"},
+ support: `type S struct { p *int; a [4]byte }`,
+ body: `i := 0; p := &S{p:&i}; C.f(C.PV(unsafe.Pointer(&p.a[0])))`,
+ fail: false,
+ },
{
// Passing the address of a static variable with no
// pointers doesn't matter.
--- /dev/null
+! Copyright 2016 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.
+
+function the_answer() result(j) bind(C)
+ use iso_c_binding, only: c_int
+ integer(c_int) :: j ! output
+ j = 42
+end function the_answer
--- /dev/null
+// Copyright 2016 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 fortran
+
+// int the_answer();
+import "C"
+
+func TheAnswer() int {
+ return int(C.the_answer())
+}
--- /dev/null
+// Copyright 2016 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 fortran
+
+import "testing"
+
+func TestFortran(t *testing.T) {
+ if a := TheAnswer(); a != 42 {
+ t.Errorf("Unexpected result for The Answer. Got: %d Want: 42", a)
+ }
+}
--- /dev/null
+ program HelloWorldF90
+ write(*,*) "Hello World!"
+ end program HelloWorldF90
--- /dev/null
+#!/usr/bin/env bash
+# Copyright 2016 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# This directory is intended to test the use of Fortran with cgo.
+
+set -e
+
+FC=$1
+
+if ! $FC helloworld/helloworld.f90 -o main.exe >& /dev/null; then
+ echo "skipping Fortran test: could not build helloworld.f90 with $FC"
+ exit 0
+fi
+
+if ! go test; then
+ echo "FAIL: go test"
+ status=1
+fi
+
+exit $status
if [ ! -f src/libgo/libgo.go ]; then
cwd=$(pwd)
- echo 'misc/cgo/testcshared/test.bash is running in $cwd' 1>&2
+ echo "misc/cgo/testcshared/test.bash is running in $cwd" 1>&2
exit 1
fi
#
# This script does not handle file names that contain spaces.
-gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$')
+gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$')
[ -z "$gofiles" ] && exit 0
unformatted=$(gofmt -l $gofiles)
}
}
if err == io.EOF {
- // If we filled the buffer exactly, flush pre-emptively.
+ // If we filled the buffer exactly, flush preemptively.
if b.Available() == 0 {
err = b.flush()
} else {
// Test that an EOF is overridden by a user-generated scan error.
func TestErrAtEOF(t *testing.T) {
s := NewScanner(strings.NewReader("1 2 33"))
- // This spitter will fail on last entry, after s.err==EOF.
+ // This splitter will fail on last entry, after s.err==EOF.
split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
advance, token, err = ScanWords(data, atEOF)
if len(token) > 1 {
pattern=.
fi
-# put linux, nacl first in the target list to get all the architectures up front.
-targets="$((ls runtime | sed -n 's/^rt0_\(.*\)_\(.*\)\.s/\1-\2/p'; echo linux-386-387 linux-arm-arm5) | sort | sed -e 's|linux-mips64x|linux-mips64 linux-mips64le|' | egrep -v android-arm | egrep "$pattern" | egrep 'linux|nacl')
-$(ls runtime | sed -n 's/^rt0_\(.*\)_\(.*\)\.s/\1-\2/p' | egrep -v 'android-arm|darwin-arm' | egrep "$pattern" | egrep -v 'linux|nacl')"
-
./make.bash || exit 1
GOROOT="$(cd .. && pwd)"
+# put linux, nacl first in the target list to get all the architectures up front.
+targets="$((../bin/go tool dist list | sed -n 's/^\(.*\)\/\(.*\)/\1-\2/p'; echo linux-386-387 linux-arm-arm5) | sort | egrep -v android-arm | egrep "$pattern" | egrep 'linux|nacl')
+$(../bin/go tool dist list | sed -n 's/^\(.*\)\/\(.*\)/\1-\2/p' | egrep -v 'android-arm|darwin-arm' | egrep "$pattern" | egrep -v 'linux|nacl')"
+
failed=false
for target in $targets
do
}
}
+// test a small index across all page offsets
+func TestIndexByteSmall(t *testing.T) {
+ b := make([]byte, 5015) // bigger than a page
+ // Make sure we find the correct byte even when straddling a page.
+ for i := 0; i <= len(b)-15; i++ {
+ for j := 0; j < 15; j++ {
+ b[i+j] = byte(100 + j)
+ }
+ for j := 0; j < 15; j++ {
+ p := IndexByte(b[i:i+15], byte(100+j))
+ if p != j {
+ t.Errorf("IndexByte(%q, %d) = %d", b[i:i+15], 100+j, p)
+ }
+ }
+ for j := 0; j < 15; j++ {
+ b[i+j] = 0
+ }
+ }
+ // Make sure matches outside the slice never trigger.
+ for i := 0; i <= len(b)-15; i++ {
+ for j := 0; j < 15; j++ {
+ b[i+j] = 1
+ }
+ for j := 0; j < 15; j++ {
+ p := IndexByte(b[i:i+15], byte(0))
+ if p != -1 {
+ t.Errorf("IndexByte(%q, %d) = %d", b[i:i+15], 0, p)
+ }
+ }
+ for j := 0; j < 15; j++ {
+ b[i+j] = 0
+ }
+ }
+}
+
func TestIndexRune(t *testing.T) {
for _, tt := range indexRuneTests {
a := []byte(tt.a)
var bmbuf []byte
+func BenchmarkIndexByte10(b *testing.B) { bmIndexByte(b, IndexByte, 10) }
func BenchmarkIndexByte32(b *testing.B) { bmIndexByte(b, IndexByte, 32) }
func BenchmarkIndexByte4K(b *testing.B) { bmIndexByte(b, IndexByte, 4<<10) }
func BenchmarkIndexByte4M(b *testing.B) { bmIndexByte(b, IndexByte, 4<<20) }
func BenchmarkIndexByte64M(b *testing.B) { bmIndexByte(b, IndexByte, 64<<20) }
+func BenchmarkIndexBytePortable10(b *testing.B) { bmIndexByte(b, IndexBytePortable, 10) }
func BenchmarkIndexBytePortable32(b *testing.B) { bmIndexByte(b, IndexBytePortable, 32) }
func BenchmarkIndexBytePortable4K(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<10) }
func BenchmarkIndexBytePortable4M(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<20) }
Flags:
-D value
- predefined symbol with optional simple value -D=identifer=value;
+ predefined symbol with optional simple value -D=identifier=value;
can be set multiple times
-I value
include directory; can be set multiple times
t.Errorf(format, args...)
ok = false
}
- obj.Flushplist(ctxt)
+ obj.FlushplistNoFree(ctxt)
for p := top; p != nil; p = p.Link {
if p.As == obj.ATEXT {
p.errorf("illegal use of register list")
}
p.registerList(a)
- p.expect(scanner.EOF)
+ p.expectOperandEnd()
return true
}
}
}
// fmt.Printf("REG %s\n", obj.Dconv(&emptyProg, 0, a))
- p.expect(scanner.EOF)
+ p.expectOperandEnd()
return true
}
a.Type = obj.TYPE_FCONST
a.Val = p.floatExpr()
// fmt.Printf("FCONST %s\n", obj.Dconv(&emptyProg, 0, a))
- p.expect(scanner.EOF)
+ p.expectOperandEnd()
return true
}
if p.have(scanner.String) {
a.Type = obj.TYPE_SCONST
a.Val = str
// fmt.Printf("SCONST %s\n", obj.Dconv(&emptyProg, 0, a))
- p.expect(scanner.EOF)
+ p.expectOperandEnd()
return true
}
a.Offset = int64(p.expr())
a.Type = obj.TYPE_MEM
}
// fmt.Printf("CONST %d %s\n", a.Offset, obj.Dconv(&emptyProg, 0, a))
- p.expect(scanner.EOF)
+ p.expectOperandEnd()
return true
}
// fmt.Printf("offset %d \n", a.Offset)
p.registerIndirect(a, prefix)
// fmt.Printf("DONE %s\n", p.arch.Dconv(&emptyProg, 0, a))
- p.expect(scanner.EOF)
+ p.expectOperandEnd()
return true
}
// get verifies that the next item has the expected type and returns it.
func (p *Parser) get(expected lex.ScanToken) lex.Token {
- p.expect(expected)
+ p.expect(expected, expected.String())
return p.next()
}
+// expectOperandEnd verifies that the parsing state is properly at the end of an operand.
+func (p *Parser) expectOperandEnd() {
+ p.expect(scanner.EOF, "end of operand")
+}
+
// expect verifies that the next item has the expected type. It does not consume it.
-func (p *Parser) expect(expected lex.ScanToken) {
- if p.peek() != expected {
- p.errorf("expected %s, found %s", expected, p.next())
+func (p *Parser) expect(expectedToken lex.ScanToken, expectedMessage string) {
+ if p.peek() != expectedToken {
+ p.errorf("expected %s, found %s", expectedMessage, p.next())
}
}
{"TEXT", "%", "expect two or three operands for TEXT"},
{"TEXT", "1, 1", "TEXT symbol \"<erroneous symbol>\" must be a symbol(SB)"},
{"TEXT", "$\"foo\", 0, $1", "TEXT symbol \"<erroneous symbol>\" must be a symbol(SB)"},
- {"TEXT", "$0É:0, 0, $1", "expected EOF, found É"}, // Issue #12467.
- {"TEXT", "$:0:(SB, 0, $1", "expected '(', found 0"}, // Issue 12468.
+ {"TEXT", "$0É:0, 0, $1", "expected end of operand, found É"}, // Issue #12467.
+ {"TEXT", "$:0:(SB, 0, $1", "expected '(', found 0"}, // Issue 12468.
{"FUNCDATA", "", "expect two operands for FUNCDATA"},
{"FUNCDATA", "(SB ", "expect two operands for FUNCDATA"},
{"DATA", "", "expect two operands for DATA"},
//
// LSTXR reg ',' addr ',' reg
// {
-// outtcode($1, &$2, &$4, &$6);
+// outcode($1, &$2, &$4, &$6);
// }
LDAXRW (R0), R2
STLXRW R1, (R0), R3
)
func init() {
- flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifer=value; can be set multiple times")
+ flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifier=value; can be set multiple times")
flag.Var(&I, "I", "include directory; can be set multiple times")
}
peekText string
}
-// NewInput returns a
+// NewInput returns an Input from the given path.
func NewInput(name string) *Input {
return &Input{
// include directories: look in source dir, then -I directories.
input := NewInput(name)
fd, err := os.Open(name)
if err != nil {
- log.Fatalf("asm: %s\n", err)
+ log.Fatalf("%s\n", err)
}
input.Push(NewTokenizer(name, fd, fd))
return input
architecture := arch.Set(GOARCH)
if architecture == nil {
- log.Fatalf("asm: unrecognized architecture %s", GOARCH)
+ log.Fatalf("unrecognized architecture %s", GOARCH)
}
flags.Parse()
obj.Writeobjdirect(ctxt, output)
}
if !ok || diag {
- log.Printf("asm: assembly of %s failed", flag.Arg(0))
+ log.Printf("assembly of %s failed", flag.Arg(0))
os.Remove(*flags.OutputFile)
os.Exit(1)
}
"C? Go? Cgo!" for an introduction to using cgo:
https://golang.org/doc/articles/c_go_cgo.html.
-CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS may be defined with pseudo #cgo
-directives within these comments to tweak the behavior of the C or C++
-compiler. Values defined in multiple directives are concatenated
+CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS and LDFLAGS may be defined with pseudo
+#cgo directives within these comments to tweak the behavior of the C, C++
+or Fortran compiler. Values defined in multiple directives are concatenated
together. The directive can include a list of build constraints limiting its
effect to systems satisfying one of the constraints
(see https://golang.org/pkg/go/build/#hdr-Build_Constraints for details about the constraint syntax).
// #include <png.h>
import "C"
-When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS and
+When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS and
CGO_LDFLAGS environment variables are added to the flags derived from
these directives. Package-specific flags should be set using the
directives, not the environment variables, so that builds work in
All the cgo CPPFLAGS and CFLAGS directives in a package are concatenated and
used to compile C files in that package. All the CPPFLAGS and CXXFLAGS
directives in a package are concatenated and used to compile C++ files in that
-package. All the LDFLAGS directives in any package in the program are
-concatenated and used at link time. All the pkg-config directives are
-concatenated and sent to pkg-config simultaneously to add to each appropriate
-set of command-line flags.
+package. All the CPPFLAGS and FFLAGS directives in a package are concatenated
+and used to compile Fortran files in that package. All the LDFLAGS directives
+in any package in the program are concatenated and used at link time. All the
+pkg-config directives are concatenated and sent to pkg-config simultaneously
+to add to each appropriate set of command-line flags.
When the cgo directives are parsed, any occurrence of the string ${SRCDIR}
will be replaced by the absolute path to the directory containing the source
"C", it will look for other non-Go files in the directory and compile
them as part of the Go package. Any .c, .s, or .S files will be
compiled with the C compiler. Any .cc, .cpp, or .cxx files will be
-compiled with the C++ compiler. Any .h, .hh, .hpp, or .hxx files will
+compiled with the C++ compiler. Any .f, .F, .for or .f90 files will be
+compiled with the fortran compiler. Any .h, .hh, .hpp, or .hxx files will
not be compiled separately, but, if these header files are changed,
the C and C++ files will be recompiled. The default C and C++
compilers may be changed by the CC and CXX environment variables,
func (p *Package) isType(t ast.Expr) bool {
switch t := t.(type) {
case *ast.SelectorExpr:
- if t.Sel.Name != "Pointer" {
- return false
- }
id, ok := t.X.(*ast.Ident)
if !ok {
return false
}
- return id.Name == "unsafe"
+ if id.Name == "unsafe" && t.Sel.Name == "Pointer" {
+ return true
+ }
+ if id.Name == "C" && typedef["_Ctype_"+t.Sel.Name] != nil {
+ return true
+ }
+ return false
case *ast.Ident:
// TODO: This ignores shadowing.
switch t.Name {
"cmd/internal/obj/x86"
)
-func defframe(ptxt *obj.Prog) {
- var n *gc.Node
+// no floating point in note handlers on Plan 9
+var isPlan9 = obj.Getgoos() == "plan9"
+func defframe(ptxt *obj.Prog) {
// fill in argument size, stack size
ptxt.To.Type = obj.TYPE_TEXTSIZE
x0 := uint32(0)
// iterate through declarations - they are sorted in decreasing xoffset order.
- for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
- n = l.N
+ for _, n := range gc.Curfn.Func.Dcl {
if !n.Name.Needzero {
continue
}
*ax = 1
}
p = appendpp(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo)
- } else if cnt <= int64(8*gc.Widthreg) {
+ } else if !isPlan9 && cnt <= int64(8*gc.Widthreg) {
if *x0 == 0 {
p = appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
*x0 = 1
if cnt%16 != 0 {
p = appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+cnt-int64(16))
}
- } else if !gc.Nacl && (cnt <= int64(128*gc.Widthreg)) {
+ } else if !gc.Nacl && !isPlan9 && (cnt <= int64(128*gc.Widthreg)) {
if *x0 == 0 {
p = appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
*x0 = 1
}
-
p = appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo+dzDI(cnt), obj.TYPE_REG, x86.REG_DI, 0)
p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, dzOff(cnt))
p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg))
w := nl.Type.Width
- if w > 1024 || (gc.Nacl && w >= 64) {
+ if w > 1024 || (w >= 64 && (gc.Nacl || isPlan9)) {
var oldn1 gc.Node
var n1 gc.Node
savex(x86.REG_DI, &n1, &oldn1, nil, gc.Types[gc.Tptr])
}
func clearfat_tail(n1 *gc.Node, b int64) {
+ if b >= 16 && isPlan9 {
+ var z gc.Node
+ gc.Nodconst(&z, gc.Types[gc.TUINT64], 0)
+ q := b / 8
+ for ; q > 0; q-- {
+ n1.Type = z.Type
+ gins(x86.AMOVQ, &z, n1)
+ n1.Xoffset += 8
+ b -= 8
+ }
+ if b != 0 {
+ n1.Xoffset -= 8 - b
+ gins(x86.AMOVQ, &z, n1)
+ }
+ return
+ }
if b >= 16 {
var vec_zero gc.Node
gc.Regalloc(&vec_zero, gc.Types[gc.TFLOAT64], nil)
}
if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST {
- // move or artihmetic into partial register.
+ // move or arithmetic into partial register.
// from another register or constant can be movl.
// we don't switch to 64-bit arithmetic if it can
// change how the carry bit is set (and the carry bit is needed).
)
func defframe(ptxt *obj.Prog) {
- var n *gc.Node
-
// fill in argument size, stack size
ptxt.To.Type = obj.TYPE_TEXTSIZE
hi := int64(0)
lo := hi
r0 := uint32(0)
- for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
- n = l.N
+ for _, n := range gc.Curfn.Func.Dcl {
if !n.Name.Needzero {
continue
}
)
func defframe(ptxt *obj.Prog) {
- var n *gc.Node
-
// fill in argument size, stack size
ptxt.To.Type = obj.TYPE_TEXTSIZE
lo := hi
// iterate through declarations - they are sorted in decreasing xoffset order.
- for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
- n = l.N
+ for _, n := range gc.Curfn.Func.Dcl {
if !n.Name.Needzero {
continue
}
// The hardware will generate undefined result.
// Also need to explicitly trap on division on zero,
// the hardware will silently generate undefined result.
- // DIVW will leave unpredicable result in higher 32-bit,
+ // DIVW will leave unpredictable result in higher 32-bit,
// so always use DIVD/DIVDU.
t := nl.Type
--- /dev/null
+// Copyright 2016 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 gc
+
+import "fmt"
+
+const (
+ // These values are known by runtime.
+ ANOEQ = iota
+ AMEM0
+ AMEM8
+ AMEM16
+ AMEM32
+ AMEM64
+ AMEM128
+ ASTRING
+ AINTER
+ ANILINTER
+ AFLOAT32
+ AFLOAT64
+ ACPLX64
+ ACPLX128
+ AMEM = 100
+)
+
+func algtype(t *Type) int {
+ a := algtype1(t, nil)
+ if a == AMEM {
+ switch t.Width {
+ case 0:
+ return AMEM0
+ case 1:
+ return AMEM8
+ case 2:
+ return AMEM16
+ case 4:
+ return AMEM32
+ case 8:
+ return AMEM64
+ case 16:
+ return AMEM128
+ }
+ }
+
+ return a
+}
+
+func algtype1(t *Type, bad **Type) int {
+ if bad != nil {
+ *bad = nil
+ }
+ if t.Broke {
+ return AMEM
+ }
+ if t.Noalg {
+ return ANOEQ
+ }
+
+ switch t.Etype {
+ // will be defined later.
+ case TANY, TFORW:
+ *bad = t
+
+ return -1
+
+ case TINT8,
+ TUINT8,
+ TINT16,
+ TUINT16,
+ TINT32,
+ TUINT32,
+ TINT64,
+ TUINT64,
+ TINT,
+ TUINT,
+ TUINTPTR,
+ TBOOL,
+ TPTR32,
+ TPTR64,
+ TCHAN,
+ TUNSAFEPTR:
+ return AMEM
+
+ case TFUNC, TMAP:
+ if bad != nil {
+ *bad = t
+ }
+ return ANOEQ
+
+ case TFLOAT32:
+ return AFLOAT32
+
+ case TFLOAT64:
+ return AFLOAT64
+
+ case TCOMPLEX64:
+ return ACPLX64
+
+ case TCOMPLEX128:
+ return ACPLX128
+
+ case TSTRING:
+ return ASTRING
+
+ case TINTER:
+ if isnilinter(t) {
+ return ANILINTER
+ }
+ return AINTER
+
+ case TARRAY:
+ if Isslice(t) {
+ if bad != nil {
+ *bad = t
+ }
+ return ANOEQ
+ }
+
+ a := algtype1(t.Type, bad)
+ if a == ANOEQ || a == AMEM {
+ if a == ANOEQ && bad != nil {
+ *bad = t
+ }
+ return a
+ }
+
+ switch t.Bound {
+ case 0:
+ // We checked above that the element type is comparable.
+ return AMEM
+ case 1:
+ // Single-element array is same as its lone element.
+ return a
+ }
+
+ return -1 // needs special compare
+
+ case TSTRUCT:
+ if t.Type != nil && t.Type.Down == nil && !isblanksym(t.Type.Sym) {
+ // One-field struct is same as that one field alone.
+ return algtype1(t.Type.Type, bad)
+ }
+
+ ret := AMEM
+ var a int
+ for t1 := t.Type; t1 != nil; t1 = t1.Down {
+ // All fields must be comparable.
+ a = algtype1(t1.Type, bad)
+
+ if a == ANOEQ {
+ return ANOEQ
+ }
+
+ // Blank fields, padded fields, fields with non-memory
+ // equality need special compare.
+ if a != AMEM || isblanksym(t1.Sym) || ispaddedfield(t1, t.Width) {
+ ret = -1
+ continue
+ }
+ }
+
+ return ret
+ }
+
+ Fatalf("algtype1: unexpected type %v", t)
+ return 0
+}
+
+// Generate a helper function to compute the hash of a value of type t.
+func genhash(sym *Sym, t *Type) {
+ if Debug['r'] != 0 {
+ fmt.Printf("genhash %v %v\n", sym, t)
+ }
+
+ lineno = 1 // less confusing than end of input
+ dclcontext = PEXTERN
+ markdcl()
+
+ // func sym(p *T, h uintptr) uintptr
+ fn := Nod(ODCLFUNC, nil, nil)
+
+ fn.Func.Nname = newname(sym)
+ fn.Func.Nname.Class = PFUNC
+ tfn := Nod(OTFUNC, nil, nil)
+ fn.Func.Nname.Name.Param.Ntype = tfn
+
+ n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
+ tfn.List = list(tfn.List, n)
+ np := n.Left
+ n = Nod(ODCLFIELD, newname(Lookup("h")), typenod(Types[TUINTPTR]))
+ tfn.List = list(tfn.List, n)
+ nh := n.Left
+ n = Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])) // return value
+ tfn.Rlist = list(tfn.Rlist, n)
+
+ funchdr(fn)
+ typecheck(&fn.Func.Nname.Name.Param.Ntype, Etype)
+
+ // genhash is only called for types that have equality but
+ // cannot be handled by the standard algorithms,
+ // so t must be either an array or a struct.
+ switch t.Etype {
+ default:
+ Fatalf("genhash %v", t)
+
+ case TARRAY:
+ if Isslice(t) {
+ Fatalf("genhash %v", t)
+ }
+
+ // An array of pure memory would be handled by the
+ // standard algorithm, so the element type must not be
+ // pure memory.
+ hashel := hashfor(t.Type)
+
+ n := Nod(ORANGE, nil, Nod(OIND, np, nil))
+ ni := newname(Lookup("i"))
+ ni.Type = Types[TINT]
+ n.List = list1(ni)
+ n.Colas = true
+ colasdefn(n.List, n)
+ ni = n.List.N
+
+ // h = hashel(&p[i], h)
+ call := Nod(OCALL, hashel, nil)
+
+ nx := Nod(OINDEX, np, ni)
+ nx.Bounded = true
+ na := Nod(OADDR, nx, nil)
+ na.Etype = 1 // no escape to heap
+ call.List = list(call.List, na)
+ call.List = list(call.List, nh)
+ n.Nbody = list(n.Nbody, Nod(OAS, nh, call))
+
+ fn.Nbody = list(fn.Nbody, n)
+
+ // Walk the struct using memhash for runs of AMEM
+ // and calling specific hash functions for the others.
+ case TSTRUCT:
+ var call *Node
+ var nx *Node
+ var na *Node
+ var hashel *Node
+
+ t1 := t.Type
+ for {
+ first, size, next := memrun(t, t1)
+ t1 = next
+
+ // Run memhash for fields up to this one.
+ if first != nil {
+ hashel = hashmem(first.Type)
+
+ // h = hashel(&p.first, size, h)
+ call = Nod(OCALL, hashel, nil)
+
+ nx = Nod(OXDOT, np, newname(first.Sym)) // TODO: fields from other packages?
+ na = Nod(OADDR, nx, nil)
+ na.Etype = 1 // no escape to heap
+ call.List = list(call.List, na)
+ call.List = list(call.List, nh)
+ call.List = list(call.List, Nodintconst(size))
+ fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
+ }
+
+ if t1 == nil {
+ break
+ }
+ if isblanksym(t1.Sym) {
+ t1 = t1.Down
+ continue
+ }
+ if algtype1(t1.Type, nil) == AMEM {
+ // Our memory run might have been stopped by padding or a blank field.
+ // If the next field is memory-ish, it could be the start of a new run.
+ continue
+ }
+
+ hashel = hashfor(t1.Type)
+ call = Nod(OCALL, hashel, nil)
+ nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages?
+ na = Nod(OADDR, nx, nil)
+ na.Etype = 1 // no escape to heap
+ call.List = list(call.List, na)
+ call.List = list(call.List, nh)
+ fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
+
+ t1 = t1.Down
+ }
+ }
+
+ r := Nod(ORETURN, nil, nil)
+ r.List = list(r.List, nh)
+ fn.Nbody = list(fn.Nbody, r)
+
+ if Debug['r'] != 0 {
+ dumplist("genhash body", fn.Nbody)
+ }
+
+ funcbody(fn)
+ Curfn = fn
+ fn.Func.Dupok = true
+ typecheck(&fn, Etop)
+ typechecklist(fn.Nbody, Etop)
+ Curfn = nil
+
+ // Disable safemode while compiling this code: the code we
+ // generate internally can refer to unsafe.Pointer.
+ // In this case it can happen if we need to generate an ==
+ // for a struct containing a reflect.Value, which itself has
+ // an unexported field of type unsafe.Pointer.
+ old_safemode := safemode
+
+ safemode = 0
+ funccompile(fn)
+ safemode = old_safemode
+}
+
+func hashfor(t *Type) *Node {
+ var sym *Sym
+
+ a := algtype1(t, nil)
+ switch a {
+ case AMEM:
+ Fatalf("hashfor with AMEM type")
+
+ case AINTER:
+ sym = Pkglookup("interhash", Runtimepkg)
+
+ case ANILINTER:
+ sym = Pkglookup("nilinterhash", Runtimepkg)
+
+ case ASTRING:
+ sym = Pkglookup("strhash", Runtimepkg)
+
+ case AFLOAT32:
+ sym = Pkglookup("f32hash", Runtimepkg)
+
+ case AFLOAT64:
+ sym = Pkglookup("f64hash", Runtimepkg)
+
+ case ACPLX64:
+ sym = Pkglookup("c64hash", Runtimepkg)
+
+ case ACPLX128:
+ sym = Pkglookup("c128hash", Runtimepkg)
+
+ default:
+ sym = typesymprefix(".hash", t)
+ }
+
+ n := newname(sym)
+ n.Class = PFUNC
+ tfn := Nod(OTFUNC, nil, nil)
+ tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
+ tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
+ tfn.Rlist = list(tfn.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
+ typecheck(&tfn, Etype)
+ n.Type = tfn.Type
+ return n
+}
+
+// geneq generates a helper function to
+// check equality of two values of type t.
+func geneq(sym *Sym, t *Type) {
+ if Debug['r'] != 0 {
+ fmt.Printf("geneq %v %v\n", sym, t)
+ }
+
+ lineno = 1 // less confusing than end of input
+ dclcontext = PEXTERN
+ markdcl()
+
+ // func sym(p, q *T) bool
+ fn := Nod(ODCLFUNC, nil, nil)
+
+ fn.Func.Nname = newname(sym)
+ fn.Func.Nname.Class = PFUNC
+ tfn := Nod(OTFUNC, nil, nil)
+ fn.Func.Nname.Name.Param.Ntype = tfn
+
+ n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
+ tfn.List = list(tfn.List, n)
+ np := n.Left
+ n = Nod(ODCLFIELD, newname(Lookup("q")), typenod(Ptrto(t)))
+ tfn.List = list(tfn.List, n)
+ nq := n.Left
+ n = Nod(ODCLFIELD, nil, typenod(Types[TBOOL]))
+ tfn.Rlist = list(tfn.Rlist, n)
+
+ funchdr(fn)
+
+ // geneq is only called for types that have equality but
+ // cannot be handled by the standard algorithms,
+ // so t must be either an array or a struct.
+ switch t.Etype {
+ default:
+ Fatalf("geneq %v", t)
+
+ case TARRAY:
+ if Isslice(t) {
+ Fatalf("geneq %v", t)
+ }
+
+ // An array of pure memory would be handled by the
+ // standard memequal, so the element type must not be
+ // pure memory. Even if we unrolled the range loop,
+ // each iteration would be a function call, so don't bother
+ // unrolling.
+ nrange := Nod(ORANGE, nil, Nod(OIND, np, nil))
+
+ ni := newname(Lookup("i"))
+ ni.Type = Types[TINT]
+ nrange.List = list1(ni)
+ nrange.Colas = true
+ colasdefn(nrange.List, nrange)
+ ni = nrange.List.N
+
+ // if p[i] != q[i] { return false }
+ nx := Nod(OINDEX, np, ni)
+
+ nx.Bounded = true
+ ny := Nod(OINDEX, nq, ni)
+ ny.Bounded = true
+
+ nif := Nod(OIF, nil, nil)
+ nif.Left = Nod(ONE, nx, ny)
+ r := Nod(ORETURN, nil, nil)
+ r.List = list(r.List, Nodbool(false))
+ nif.Nbody = list(nif.Nbody, r)
+ nrange.Nbody = list(nrange.Nbody, nif)
+ fn.Nbody = list(fn.Nbody, nrange)
+
+ // return true
+ ret := Nod(ORETURN, nil, nil)
+ ret.List = list(ret.List, Nodbool(true))
+ fn.Nbody = list(fn.Nbody, ret)
+
+ // Walk the struct using memequal for runs of AMEM
+ // and calling specific equality tests for the others.
+ // Skip blank-named fields.
+ case TSTRUCT:
+ var conjuncts []*Node
+
+ t1 := t.Type
+ for {
+ first, size, next := memrun(t, t1)
+ t1 = next
+
+ // Run memequal for fields up to this one.
+ // TODO(rsc): All the calls to newname are wrong for
+ // cross-package unexported fields.
+ if first != nil {
+ if first.Down == t1 {
+ conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
+ } else if first.Down.Down == t1 {
+ conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
+ first = first.Down
+ if !isblanksym(first.Sym) {
+ conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
+ }
+ } else {
+ // More than two fields: use memequal.
+ conjuncts = append(conjuncts, eqmem(np, nq, newname(first.Sym), size))
+ }
+ }
+
+ if t1 == nil {
+ break
+ }
+ if isblanksym(t1.Sym) {
+ t1 = t1.Down
+ continue
+ }
+ if algtype1(t1.Type, nil) == AMEM {
+ // Our memory run might have been stopped by padding or a blank field.
+ // If the next field is memory-ish, it could be the start of a new run.
+ continue
+ }
+
+ // Check this field, which is not just memory.
+ conjuncts = append(conjuncts, eqfield(np, nq, newname(t1.Sym)))
+ t1 = t1.Down
+ }
+
+ var and *Node
+ switch len(conjuncts) {
+ case 0:
+ and = Nodbool(true)
+ case 1:
+ and = conjuncts[0]
+ default:
+ and = Nod(OANDAND, conjuncts[0], conjuncts[1])
+ for _, conjunct := range conjuncts[2:] {
+ and = Nod(OANDAND, and, conjunct)
+ }
+ }
+
+ ret := Nod(ORETURN, nil, nil)
+ ret.List = list(ret.List, and)
+ fn.Nbody = list(fn.Nbody, ret)
+ }
+
+ if Debug['r'] != 0 {
+ dumplist("geneq body", fn.Nbody)
+ }
+
+ funcbody(fn)
+ Curfn = fn
+ fn.Func.Dupok = true
+ typecheck(&fn, Etop)
+ typechecklist(fn.Nbody, Etop)
+ Curfn = nil
+
+ // Disable safemode while compiling this code: the code we
+ // generate internally can refer to unsafe.Pointer.
+ // In this case it can happen if we need to generate an ==
+ // for a struct containing a reflect.Value, which itself has
+ // an unexported field of type unsafe.Pointer.
+ old_safemode := safemode
+ safemode = 0
+
+ // Disable checknils while compiling this code.
+ // We are comparing a struct or an array,
+ // neither of which can be nil, and our comparisons
+ // are shallow.
+ Disable_checknil++
+
+ funccompile(fn)
+
+ safemode = old_safemode
+ Disable_checknil--
+}
+
+// eqfield returns the node
+// p.field == q.field
+func eqfield(p *Node, q *Node, field *Node) *Node {
+ nx := Nod(OXDOT, p, field)
+ ny := Nod(OXDOT, q, field)
+ ne := Nod(OEQ, nx, ny)
+ return ne
+}
+
+// eqmem returns the node
+// memequal(&p.field, &q.field [, size])
+func eqmem(p *Node, q *Node, field *Node, size int64) *Node {
+ var needsize int
+
+ nx := Nod(OADDR, Nod(OXDOT, p, field), nil)
+ nx.Etype = 1 // does not escape
+ ny := Nod(OADDR, Nod(OXDOT, q, field), nil)
+ ny.Etype = 1 // does not escape
+ typecheck(&nx, Erv)
+ typecheck(&ny, Erv)
+
+ call := Nod(OCALL, eqmemfunc(size, nx.Type.Type, &needsize), nil)
+ call.List = list(call.List, nx)
+ call.List = list(call.List, ny)
+ if needsize != 0 {
+ call.List = list(call.List, Nodintconst(size))
+ }
+
+ return call
+}
+
+func eqmemfunc(size int64, type_ *Type, needsize *int) *Node {
+ var fn *Node
+
+ switch size {
+ default:
+ fn = syslook("memequal", 1)
+ *needsize = 1
+
+ case 1, 2, 4, 8, 16:
+ buf := fmt.Sprintf("memequal%d", int(size)*8)
+ fn = syslook(buf, 1)
+ *needsize = 0
+ }
+
+ substArgTypes(fn, type_, type_)
+ return fn
+}
+
+// memrun finds runs of struct fields for which memory-only algs are appropriate.
+// t is the parent struct type, and field is the field at which to start.
+// first is the first field in the memory run.
+// size is the length in bytes of the memory included in the run.
+// next is the next field after the memory run.
+func memrun(t *Type, field *Type) (first *Type, size int64, next *Type) {
+ var offend int64
+ for {
+ if field == nil || algtype1(field.Type, nil) != AMEM || isblanksym(field.Sym) {
+ break
+ }
+ offend = field.Width + field.Type.Width
+ if first == nil {
+ first = field
+ }
+
+ // If it's a memory field but it's padded, stop here.
+ if ispaddedfield(field, t.Width) {
+ field = field.Down
+ break
+ }
+ field = field.Down
+ }
+ if first != nil {
+ size = offend - first.Width // first.Width is offset
+ }
+ return first, size, field
+}
+
+// ispaddedfield reports whether the given field
+// is followed by padding. For the case where t is
+// the last field, total gives the size of the enclosing struct.
+func ispaddedfield(t *Type, total int64) bool {
+ if t.Etype != TFIELD {
+ Fatalf("ispaddedfield called non-field %v", t)
+ }
+ if t.Down == nil {
+ return t.Width+t.Type.Width != total
+ }
+ return t.Width+t.Type.Width != t.Down.Width
+}
All integer values use a variable-length encoding for compact representation.
-If debugFormat is set, each integer and string value is preceeded by a marker
+If debugFormat is set, each integer and string value is preceded by a marker
and position information in the encoding. This mechanism permits an importer
to recognize immediately when it is out of sync. The importer recognizes this
mode automatically (i.e., it can import export data produced with debugging
{
saved := structpkg
structpkg = tsym.Pkg
- addmethod(sym, n.Type, false, nointerface)
+ addmethod(sym, n.Type, false, false)
structpkg = saved
}
- nointerface = false
funchdr(n)
// (comment from go.y)
func cgen_wbptr(n, res *Node) {
if Curfn != nil {
- if Curfn.Func.Nowritebarrier {
+ if Curfn.Func.Pragma&Nowritebarrier != 0 {
Yyerror("write barrier prohibited")
}
if Curfn.Func.WBLineno == 0 {
func cgen_wbfat(n, res *Node) {
if Curfn != nil {
- if Curfn.Func.Nowritebarrier {
+ if Curfn.Func.Pragma&Nowritebarrier != 0 {
Yyerror("write barrier prohibited")
}
if Curfn.Func.WBLineno == 0 {
// If copying .args, that's all the results, so record definition sites
// for them for the liveness analysis.
if ns.Op == ONAME && ns.Sym.Name == ".args" {
- for l := Curfn.Func.Dcl; l != nil; l = l.Next {
- if l.N.Class == PPARAMOUT {
- Gvardef(l.N)
+ for _, ln := range Curfn.Func.Dcl {
+ if ln.Class == PPARAMOUT {
+ Gvardef(ln)
}
}
}
if hasdefer {
Ginscall(Deferreturn, 0)
}
- Genlist(Curfn.Func.Exit)
+ Genslice(Curfn.Func.Exit.Slice())
p := Thearch.Gins(obj.ARET, nil, nil)
if n != nil && n.Op == ORETJMP {
p.To.Type = obj.TYPE_MEM
// ordinary ones in the symbol table; see oldname.
// unhook them.
// make the list of pointers for the closure call.
- var v *Node
- for l := func_.Func.Cvars; l != nil; l = l.Next {
- v = l.N
+ for _, v := range func_.Func.Cvars.Slice() {
v.Name.Param.Closure.Name.Param.Closure = v.Name.Param.Outer
v.Name.Param.Outerexpr = oldname(v.Sym)
}
}
func typecheckclosure(func_ *Node, top int) {
- var n *Node
-
- for l := func_.Func.Cvars; l != nil; l = l.Next {
- n = l.N.Name.Param.Closure
+ for _, ln := range func_.Func.Cvars.Slice() {
+ n := ln.Name.Param.Closure
if !n.Name.Captured {
n.Name.Captured = true
if n.Name.Decldepth == 0 {
}
}
- for l := func_.Func.Dcl; l != nil; l = l.Next {
- if l.N.Op == ONAME && (l.N.Class == PPARAM || l.N.Class == PPARAMOUT) {
- l.N.Name.Decldepth = 1
+ for _, ln := range func_.Func.Dcl {
+ if ln.Op == ONAME && (ln.Class == PPARAM || ln.Class == PPARAMOUT) {
+ ln.Name.Decldepth = 1
}
}
makefuncsym(xfunc.Func.Nname.Sym)
xfunc.Nbody = func_.Nbody
- xfunc.Func.Dcl = concat(func_.Func.Dcl, xfunc.Func.Dcl)
+ xfunc.Func.Dcl = append(func_.Func.Dcl, xfunc.Func.Dcl...)
+ func_.Func.Dcl = nil
if xfunc.Nbody == nil {
Fatalf("empty body - won't generate any code")
}
// We use value capturing for values <= 128 bytes that are never reassigned
// after capturing (effectively constant).
func capturevars(xfunc *Node) {
- var v *Node
var outer *Node
lno := int(lineno)
lineno = xfunc.Lineno
func_ := xfunc.Func.Closure
- func_.Func.Enter = nil
- for l := func_.Func.Cvars; l != nil; l = l.Next {
- v = l.N
+ func_.Func.Enter.Set(nil)
+ for _, v := range func_.Func.Cvars.Slice() {
if v.Type == nil {
// if v->type is nil, it means v looked like it was
// going to be used in the closure but wasn't.
}
typecheck(&outer, Erv)
- func_.Func.Enter = list(func_.Func.Enter, outer)
+ func_.Func.Enter.Append(outer)
}
lineno = int32(lno)
original_dcl := xfunc.Func.Dcl
xfunc.Func.Dcl = nil
- var v *Node
var addr *Node
var fld *Type
- for l := func_.Func.Cvars; l != nil; l = l.Next {
- v = l.N
+ for _, v := range func_.Func.Cvars.Slice() {
if v.Op == OXXX {
continue
}
fld.Sym = fld.Nname.Sym
// Declare the new param and add it the first part of the input arguments.
- xfunc.Func.Dcl = list(xfunc.Func.Dcl, fld.Nname)
+ xfunc.Func.Dcl = append(xfunc.Func.Dcl, fld.Nname)
*param = fld
param = &fld.Down
}
*param = original_args
- xfunc.Func.Dcl = concat(xfunc.Func.Dcl, original_dcl)
+ xfunc.Func.Dcl = append(xfunc.Func.Dcl, original_dcl...)
// Recalculate param offsets.
if f.Type.Width > 0 {
xfunc.Type = f.Type // update type of ODCLFUNC
} else {
// The closure is not called, so it is going to stay as closure.
- nvar := 0
-
- var body *NodeList
+ var body []*Node
offset := int64(Widthptr)
var addr *Node
- var v *Node
var cv *Node
- for l := func_.Func.Cvars; l != nil; l = l.Next {
- v = l.N
+ for _, v := range func_.Func.Cvars.Slice() {
if v.Op == OXXX {
continue
}
- nvar++
// cv refers to the field inside of closure OSTRUCTLIT.
cv = Nod(OCLOSUREVAR, nil, nil)
// If it is a small variable captured by value, downgrade it to PAUTO.
v.Class = PAUTO
v.Ullman = 1
- xfunc.Func.Dcl = list(xfunc.Func.Dcl, v)
- body = list(body, Nod(OAS, v, cv))
+ xfunc.Func.Dcl = append(xfunc.Func.Dcl, v)
+ body = append(body, Nod(OAS, v, cv))
} else {
// Declare variable holding addresses taken from closure
// and initialize in entry prologue.
addr.Class = PAUTO
addr.Used = true
addr.Name.Curfn = xfunc
- xfunc.Func.Dcl = list(xfunc.Func.Dcl, addr)
+ xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr)
v.Name.Heapaddr = addr
if v.Name.Byval {
cv = Nod(OADDR, cv, nil)
}
- body = list(body, Nod(OAS, addr, cv))
+ body = append(body, Nod(OAS, addr, cv))
}
}
- typechecklist(body, Etop)
- walkstmtlist(body)
- xfunc.Func.Enter = body
- xfunc.Func.Needctxt = nvar > 0
+ if len(body) > 0 {
+ typecheckslice(body, Etop)
+ walkstmtslice(body)
+ xfunc.Func.Enter.Set(body)
+ xfunc.Func.Needctxt = true
+ }
}
lineno = int32(lno)
func walkclosure(func_ *Node, init **NodeList) *Node {
// If no closure vars, don't bother wrapping.
- if func_.Func.Cvars == nil {
+ if len(func_.Func.Cvars.Slice()) == 0 {
return func_.Func.Closure.Func.Nname
}
typ.List = list1(Nod(ODCLFIELD, newname(Lookup(".F")), typenod(Types[TUINTPTR])))
var typ1 *Node
- var v *Node
- for l := func_.Func.Cvars; l != nil; l = l.Next {
- v = l.N
+ for _, v := range func_.Func.Cvars.Slice() {
if v.Op == OXXX {
continue
}
clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil))
clos.Esc = func_.Esc
clos.Right.Implicit = true
- clos.List = concat(list1(Nod(OCFUNC, func_.Func.Closure.Func.Nname, nil)), func_.Func.Enter)
+ clos.List = concat(list1(Nod(OCFUNC, func_.Func.Closure.Func.Nname, nil)), func_.Func.Enter.NodeList())
// Force type conversion from *struct to the func type.
clos = Nod(OCONVNOP, clos, nil)
n = newname(Lookupf("a%d", i))
i++
n.Class = PPARAM
- xfunc.Func.Dcl = list(xfunc.Func.Dcl, n)
+ xfunc.Func.Dcl = append(xfunc.Func.Dcl, n)
callargs = list(callargs, n)
fld = Nod(ODCLFIELD, n, typenod(t.Type))
if t.Isddd {
n = newname(Lookupf("r%d", i))
i++
n.Class = PPARAMOUT
- xfunc.Func.Dcl = list(xfunc.Func.Dcl, n)
+ xfunc.Func.Dcl = append(xfunc.Func.Dcl, n)
retargs = list(retargs, n)
l = list(l, Nod(ODCLFIELD, n, typenod(t.Type)))
}
ptr.Used = true
ptr.Name.Curfn = xfunc
ptr.Xoffset = 0
- xfunc.Func.Dcl = list(xfunc.Func.Dcl, ptr)
+ xfunc.Func.Dcl = append(xfunc.Func.Dcl, ptr)
var body *NodeList
if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) {
ptr.Name.Param.Ntype = typenod(rcvrtype)
Fatalf("automatic outside function")
}
if Curfn != nil {
- Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
+ Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
}
if n.Op == OTYPE {
declare_typegen++
n.Name.Param.Closure = c
c.Name.Param.Closure = n
c.Xoffset = 0
- Curfn.Func.Cvars = list(Curfn.Func.Cvars, c)
+ Curfn.Func.Cvars.Append(c)
}
// return ref to closure var, not original
Funcdepth = n.Func.Depth + 1
compile(n)
Curfn = nil
+ Pc = nil
+ continpc = nil
+ breakpc = nil
Funcdepth = 0
dclcontext = PEXTERN
+ flushdata()
+ obj.Flushplist(Ctxt) // convert from Prog list to machine code
}
func funcsym(s *Sym) *Sym {
// Check nowritebarrierrec functions.
for _, n := range list {
- if !n.Func.Nowritebarrierrec {
+ if n.Func.Pragma&Nowritebarrierrec == 0 {
continue
}
call, hasWB := c.best[n]
savefn := Curfn
Curfn = func_
- for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
- if ll.N.Op != ONAME {
+ for _, ln := range Curfn.Func.Dcl {
+ if ln.Op != ONAME {
continue
}
- llNE := e.nodeEscState(ll.N)
- switch ll.N.Class {
+ llNE := e.nodeEscState(ln)
+ switch ln.Class {
// out params are in a loopdepth between the sink and all local variables
case PPARAMOUT:
llNE.Escloopdepth = 0
case PPARAM:
llNE.Escloopdepth = 1
- if ll.N.Type != nil && !haspointers(ll.N.Type) {
+ if ln.Type != nil && !haspointers(ln.Type) {
break
}
if Curfn.Nbody == nil && !Curfn.Noescape {
- ll.N.Esc = EscHeap
+ ln.Esc = EscHeap
} else {
- ll.N.Esc = EscNone // prime for escflood later
+ ln.Esc = EscNone // prime for escflood later
}
- e.noesc = list(e.noesc, ll.N)
+ e.noesc = list(e.noesc, ln)
}
}
// in a mutually recursive group we lose track of the return values
if e.recursive {
- for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
- if ll.N.Op == ONAME && ll.N.Class == PPARAMOUT {
- escflows(e, &e.theSink, ll.N)
+ for _, ln := range Curfn.Func.Dcl {
+ if ln.Op == ONAME && ln.Class == PPARAMOUT {
+ escflows(e, &e.theSink, ln)
}
}
}
ll = e.nodeEscState(n.List.N).Escretval
}
- for lr := Curfn.Func.Dcl; lr != nil && ll != nil; lr = lr.Next {
- if lr.N.Op != ONAME || lr.N.Class != PPARAMOUT {
+ for _, lrn := range Curfn.Func.Dcl {
+ if ll == nil {
+ break
+ }
+ if lrn.Op != ONAME || lrn.Class != PPARAMOUT {
continue
}
- escassign(e, lr.N, ll.N)
+ escassign(e, lrn, ll.N)
ll = ll.Next
}
// Link addresses of captured variables to closure.
case OCLOSURE:
var a *Node
- var v *Node
- for ll := n.Func.Cvars; ll != nil; ll = ll.Next {
- v = ll.N
+ for _, v := range n.Func.Cvars.Slice() {
if v.Op == OXXX { // unnamed out argument; see dcl.go:/^funcargs
continue
}
savefn := Curfn
Curfn = func_
- for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
- if ll.N.Op != ONAME {
+ for _, ln := range Curfn.Func.Dcl {
+ if ln.Op != ONAME {
continue
}
- switch ll.N.Esc & EscMask {
+ switch ln.Esc & EscMask {
case EscNone, // not touched by escflood
EscReturn:
- if haspointers(ll.N.Type) { // don't bother tagging for scalars
- ll.N.Name.Param.Field.Note = mktag(int(ll.N.Esc))
+ if haspointers(ln.Type) { // don't bother tagging for scalars
+ ln.Name.Param.Field.Note = mktag(int(ln.Esc))
}
case EscHeap, // touched by escflood, moved to heap
return buf.String()
}
+func Hconvslice(l []*Node, flag int) string {
+ if len(l) == 0 && fmtmode == FDbg {
+ return "<nil>"
+ }
+
+ sf := flag
+ sm, sb := setfmode(&flag)
+ sep := "; "
+ if fmtmode == FDbg {
+ sep = "\n"
+ } else if flag&obj.FmtComma != 0 {
+ sep = ", "
+ }
+
+ var buf bytes.Buffer
+ for i, n := range l {
+ buf.WriteString(Nconv(n, 0))
+ if i+1 < len(l) {
+ buf.WriteString(sep)
+ }
+ }
+
+ flag = sf
+ fmtbody = sb
+ fmtmode = sm
+ return buf.String()
+}
+
func dumplist(s string, l *NodeList) {
fmt.Printf("%s%v\n", s, Hconv(l, obj.FmtSign))
}
+func dumpslice(s string, l []*Node) {
+ fmt.Printf("%s%v\n", s, Hconvslice(l, obj.FmtSign))
+}
+
func Dump(s string, n *Node) {
fmt.Printf("%s [%p]%v\n", s, n, Nconv(n, obj.FmtSign))
}
oldfn := Curfn
Curfn = n.Name.Curfn
+ if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
+ Curfn = Curfn.Func.Closure
+ }
n.Name.Heapaddr = temp(Ptrto(n.Type))
buf := fmt.Sprintf("&%v", n.Sym)
n.Name.Heapaddr.Sym = Lookup(buf)
}
}
+func Genslice(l []*Node) {
+ for _, n := range l {
+ gen(n)
+ }
+}
+
// generate code to start new proc running call n.
func cgen_proc(n *Node, proc int) {
switch n.Left.Op {
if Curfn == nil {
Fatalf("no curfn for tempname")
}
+ if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
+ Dump("Tempname", Curfn)
+ Fatalf("adding tempname to wrong closure function")
+ }
if t == nil {
Yyerror("tempname called with nil type")
n.Ullman = 1
n.Esc = EscNever
n.Name.Curfn = Curfn
- Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
+ Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
dowidth(t)
n.Xoffset = 0
"log"
"os"
"os/exec"
- "path"
+ "path/filepath"
+ "strings"
"testing"
)
defer os.RemoveAll(dir)
// Create source.
- src := path.Join(dir, "test.go")
+ src := filepath.Join(dir, "test.go")
f, err := os.Create(src)
if err != nil {
log.Fatalf("could not create source file: %v", err)
f.Close()
// Name of destination.
- dst := path.Join(dir, "test")
+ dst := filepath.Join(dir, "test")
// Compile source.
cmd := exec.Command("go", "build", "-o", dst, src)
log.Fatalf("scanf code not removed from helloworld")
}
}
+
+// Make sure -S prints assembly code. See issue 14515.
+func TestDashS(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // Make a directory to work in.
+ dir, err := ioutil.TempDir("", "issue14515-")
+ if err != nil {
+ log.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ // Create source.
+ src := filepath.Join(dir, "test.go")
+ f, err := os.Create(src)
+ if err != nil {
+ log.Fatalf("could not create source file: %v", err)
+ }
+ f.Write([]byte(`
+package main
+import "fmt"
+func main() {
+ fmt.Println("hello world")
+}
+`))
+ f.Close()
+
+ // Compile source.
+ cmd := exec.Command("go", "build", "-gcflags", "-S", "-o", filepath.Join(dir, "test"), src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ log.Fatalf("could not build target: %v", err)
+ }
+
+ patterns := []string{
+ // It is hard to look for actual instructions in an
+ // arch-independent way. So we'll just look for
+ // pseudo-ops that are arch-independent.
+ "\tTEXT\t",
+ "\tFUNCDATA\t",
+ "\tPCDATA\t",
+ }
+ outstr := string(out)
+ for _, p := range patterns {
+ if !strings.Contains(outstr, p) {
+ println(outstr)
+ panic("can't find pattern " + p)
+ }
+ }
+}
MaxStackVarSize = 10 * 1024 * 1024
)
-const (
- // These values are known by runtime.
- ANOEQ = iota
- AMEM0
- AMEM8
- AMEM16
- AMEM32
- AMEM64
- AMEM128
- ASTRING
- AINTER
- ANILINTER
- AFLOAT32
- AFLOAT64
- ACPLX64
- ACPLX128
- AMEM = 100
-)
-
const (
// Maximum size in bits for Mpints before signalling
// overflow and also mantissa precision for Mpflts.
}
type Sym struct {
- Lexical uint16
Flags uint8
- Link *Sym
Uniqgen uint32
+ Link *Sym
Importdef *Pkg // where imported definition was found
Linkname string // link name
var dotlist [10]Dlist // size is max depth of embeddeds
+// lexlineno is the line number _after_ the most recently read rune.
+// In particular, it's advanced (or rewound) as newlines are read (or unread).
var lexlineno int32
+// lineno is the line number at the start of the most recently lexed token.
var lineno int32
-var prevlineno int32
-
var pragcgobuf string
var infile string
// when the race detector is enabled.
var instrumenting bool
-// Pending annotations for next func declaration.
-var (
- noescape bool
- noinline bool
- norace bool
- nosplit bool
- nowritebarrier bool
- nowritebarrierrec bool
- systemstack bool
-)
-
var debuglive int
var Ctxt *obj.Link
-var nointerface bool
-
var writearchive int
var bstdout obj.Biobuf
Clearp(Pc)
}
+func flushdata() {
+ if dfirst == nil {
+ return
+ }
+ newplist()
+ *Pc = *dfirst
+ Pc = dpc
+ Clearp(Pc)
+ dfirst = nil
+ dpc = nil
+}
+
// Fixup instructions after allocauto (formerly compactframe) has moved all autos around.
func fixautoused(p *obj.Prog) {
for lp := &p; ; {
}
if fp == 1 || fp == -1 {
- var n *Node
- for l := Curfn.Func.Dcl; l != nil; l = l.Next {
- n = l.N
+ for _, n := range Curfn.Func.Dcl {
if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym {
return n
}
}
// If marked "go:noinline", don't inline
- if fn.Func.Noinline {
+ if fn.Func.Pragma&Noinline != 0 {
return
}
fn.Func.Nname.Func.Inl = fn.Nbody
fn.Nbody = inlcopylist(fn.Func.Nname.Func.Inl)
- fn.Func.Nname.Func.Inldcl = inlcopylist(fn.Func.Nname.Name.Defn.Func.Dcl)
+ inldcl := inlcopyslice(fn.Func.Nname.Name.Defn.Func.Dcl)
+ if len(inldcl) > 0 {
+ fn.Func.Nname.Func.Inldcl = &inldcl
+ }
fn.Func.Nname.Func.InlCost = int32(maxBudget - budget)
// hack, TODO, check for better way to link method nodes back to the thing with the ->inl
return m
}
+// Inlcopyslice is like inlcopylist, but for a slice.
+func inlcopyslice(ll []*Node) []*Node {
+ r := make([]*Node, 0, len(ll))
+ for _, ln := range ll {
+ c := inlcopy(ln)
+ if c != nil {
+ r = append(r, c)
+ }
+ }
+ return r
+}
+
// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
// calls made to inlineable functions. This is the external entry point.
func inlcalls(fn *Node) {
//dumplist("ninit pre", ninit);
- var dcl *NodeList
- if fn.Name.Defn != nil { // local function
- dcl = fn.Func.Inldcl // imported function
+ var dcl []*Node
+ if fn.Name.Defn != nil {
+ // local function
+ if fn.Func.Inldcl != nil {
+ dcl = *fn.Func.Inldcl
+ }
} else {
+ // imported function
dcl = fn.Func.Dcl
}
i := 0
// Make temp names to use instead of the originals
- for ll := dcl; ll != nil; ll = ll.Next {
- if ll.N.Class == PPARAMOUT { // return values handled below.
+ for _, ln := range dcl {
+ if ln.Class == PPARAMOUT { // return values handled below.
continue
}
- if ll.N.Op == ONAME {
- ll.N.Name.Inlvar = inlvar(ll.N)
+ if ln.Op == ONAME {
+ ln.Name.Inlvar = inlvar(ln)
// Typecheck because inlvar is not necessarily a function parameter.
- typecheck(&ll.N.Name.Inlvar, Erv)
+ typecheck(&ln.Name.Inlvar, Erv)
- if ll.N.Class&^PHEAP != PAUTO {
- ninit = list(ninit, Nod(ODCL, ll.N.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs
+ if ln.Class&^PHEAP != PAUTO {
+ ninit = list(ninit, Nod(ODCL, ln.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs
}
}
}
addrescapes(n)
}
- Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
+ Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
return n
}
n.Class = PAUTO
n.Used = true
n.Name.Curfn = Curfn // the calling function, not the called one
- Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
+ Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
return n
}
n.Class = PAUTO
n.Used = true
n.Name.Curfn = Curfn // the calling function, not the called one
- Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
+ Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
return n
}
package gc
import (
- "bytes"
"cmd/compile/internal/ssa"
"cmd/internal/obj"
"flag"
Debug_wb int
)
+const BOM = 0xFEFF
+
// Debug arguments.
// These can be specified with the -d flag, as in "-d nil"
// to set the debug_checknil variable. In general the list passed
}
Ctxt.Flag_shared = int32(flag_shared)
Ctxt.Flag_dynlink = flag_dynlink
+ Ctxt.Flag_optimize = Debug['N'] == 0
Ctxt.Debugasm = int32(Debug['S'])
Ctxt.Debugvlog = int32(Debug['v'])
dclcontext = PEXTERN
nerrors = 0
lexlineno = 1
- const BOM = 0xFEFF
loadsys()
return false
}
- // symbol table may be first; skip it
- sz := arsize(b, "__.GOSYMDEF")
-
- if sz >= 0 {
- obj.Bseek(b, int64(sz), 1)
- } else {
- obj.Bseek(b, 8, 0)
- }
-
- // package export block is next
- sz = arsize(b, "__.PKGDEF")
-
- if sz <= 0 {
- return false
- }
- return true
+ // package export block should be first
+ sz := arsize(b, "__.PKGDEF")
+ return sz > 0
}
var idirs []string
}
}
+func isDriveLetter(b byte) bool {
+ return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
+}
+
// is this path a local name? begins with ./ or ../ or /
func islocalname(name string) bool {
return strings.HasPrefix(name, "/") ||
- Ctxt.Windows != 0 && len(name) >= 3 && isAlpha(int(name[0])) && name[1] == ':' && name[2] == '/' ||
+ Ctxt.Windows != 0 && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' ||
strings.HasPrefix(name, "./") || name == "." ||
strings.HasPrefix(name, "../") || name == ".."
}
}
}
-func isSpace(c int) bool {
+func isSpace(c rune) bool {
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
}
-func isAlpha(c int) bool {
- return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
+func isLetter(c rune) bool {
+ return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_'
}
-func isDigit(c int) bool {
+func isDigit(c rune) bool {
return '0' <= c && c <= '9'
}
-func isAlnum(c int) bool {
- return isAlpha(c) || isDigit(c)
-}
func plan9quote(s string) string {
if s == "" {
return s
}
-func isfrog(c int) bool {
- // complain about possibly invisible control characters
- if c < ' ' {
- return !isSpace(c) // exclude good white space
- }
+type Pragma uint8
- if 0x7f <= c && c <= 0xa0 { // DEL, unicode block including unbreakable space.
- return true
- }
- return false
-}
+const (
+ Nointerface Pragma = 1 << iota
+ Noescape // func parameters don't escape
+ Norace // func must not have race detector annotations
+ Nosplit // func should not execute on separate stack
+ Noinline // func should not be inlined
+ Systemstack // func must run on system stack
+ Nowritebarrier // emit compiler error instead of write barrier
+ Nowritebarrierrec // error on write barrier in this or recursive callees
+)
type lexer struct {
// source
bin *obj.Biobuf
- peekc int
- peekc1 int // second peekc for ...
+ peekr1 rune
+ peekr2 rune // second peekc for ...
nlsemi bool // if set, '\n' and EOF translate to ';'
+ // pragma flags
+ // accumulated by lexer; reset by parser
+ pragma Pragma
+
// current token
tok int32
- sym_ *Sym // valid if tok == LNAME
- val Val // valid if tok == LLITERAL
- op Op // valid if tok == LASOP
+ sym_ *Sym // valid if tok == LNAME
+ val Val // valid if tok == LLITERAL
+ op Op // valid if tok == LASOP or LINCOP, or prec > 0
+ prec OpPrec // operator precedence; 0 if not a binary operator
}
+type OpPrec int
+
+const (
+ // Precedences of binary operators (must be > 0).
+ PCOMM OpPrec = 1 + iota
+ POROR
+ PANDAND
+ PCMP
+ PADD
+ PMUL
+)
+
const (
- LLITERAL = 57346 + iota
+ // The value of single-char tokens is just their character's Unicode value.
+ // They are all below utf8.RuneSelf. Shift other tokens up to avoid conflicts.
+ LLITERAL = utf8.RuneSelf + iota
LASOP
LCOLAS
LBREAK
LANDAND
LANDNOT
LCOMM
- LDEC
LEQ
LGE
LGT
LIGNORE
- LINC
+ LINCOP
LLE
LLSH
LLT
)
func (l *lexer) next() {
- var c1 int
- var op Op
- var escflag int
- var v int64
- var cp *bytes.Buffer
- var s *Sym
- var str string
-
- prevlineno = lineno
-
nlsemi := l.nlsemi
l.nlsemi = false
+ l.prec = 0
l0:
// skip white space
- c := l.getc()
+ c := l.getr()
for isSpace(c) {
if c == '\n' && nlsemi {
- l.ungetc(c)
if Debug['x'] != 0 {
fmt.Printf("lex: implicit semi\n")
}
+ // Insert implicit semicolon on previous line,
+ // before the newline character.
+ lineno = lexlineno - 1
l.tok = ';'
return
}
- c = l.getc()
+ c = l.getr()
}
// start of token
lineno = lexlineno
- if c >= utf8.RuneSelf {
- // all multibyte runes are alpha
- cp = &lexbuf
- cp.Reset()
- goto talph
- }
-
- if isAlpha(c) {
- cp = &lexbuf
- cp.Reset()
- goto talph
- }
-
- if isDigit(c) {
- cp = &lexbuf
- cp.Reset()
- if c != '0' {
- for {
- cp.WriteByte(byte(c))
- c = l.getc()
- if isDigit(c) {
- continue
- }
- if c == '.' {
- goto casedot
- }
- if c == 'e' || c == 'E' || c == 'p' || c == 'P' {
- goto caseep
- }
- if c == 'i' {
- goto casei
- }
- goto ncu
- }
- }
-
- cp.WriteByte(byte(c))
- c = l.getc()
- if c == 'x' || c == 'X' {
- for {
- cp.WriteByte(byte(c))
- c = l.getc()
- if isDigit(c) {
- continue
- }
- if c >= 'a' && c <= 'f' {
- continue
- }
- if c >= 'A' && c <= 'F' {
- continue
- }
- if lexbuf.Len() == 2 {
- Yyerror("malformed hex constant")
- }
- if c == 'p' {
- goto caseep
- }
- goto ncu
- }
+ // identifiers and keywords
+ // (for better error messages consume all chars >= utf8.RuneSelf for identifiers)
+ if isLetter(c) || c >= utf8.RuneSelf {
+ l.ident(c)
+ if l.tok == LIGNORE {
+ goto l0
}
-
- if c == 'p' { // 0p begins floating point zero
- goto caseep
- }
-
- c1 = 0
- for {
- if !isDigit(c) {
- break
- }
- if c < '0' || c > '7' {
- c1 = 1 // not octal
- }
- cp.WriteByte(byte(c))
- c = l.getc()
- }
-
- if c == '.' {
- goto casedot
- }
- if c == 'e' || c == 'E' {
- goto caseep
- }
- if c == 'i' {
- goto casei
- }
- if c1 != 0 {
- Yyerror("malformed octal constant")
- }
- goto ncu
+ return
}
+ // c < utf8.RuneSelf
+
+ var c1 rune
+ var op Op
+ var prec OpPrec
switch c {
case EOF:
- lineno = prevlineno
- l.ungetc(EOF)
+ l.ungetr(EOF) // return EOF again in future next call
// Treat EOF as "end of line" for the purposes
// of inserting a semicolon.
if nlsemi {
l.tok = -1
return
- case '_':
- cp = &lexbuf
- cp.Reset()
- goto talph
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ l.number(c)
+ return
case '.':
- c1 = l.getc()
+ c1 = l.getr()
if isDigit(c1) {
- cp = &lexbuf
- cp.Reset()
- cp.WriteByte(byte(c))
- c = c1
- goto casedot
+ l.ungetr(c1)
+ l.number('.')
+ return
}
if c1 == '.' {
- c1 = l.getc()
+ c1 = l.getr()
if c1 == '.' {
c = LDDD
goto lx
}
- l.ungetc(c1)
+ l.ungetr(c1)
c1 = '.'
}
- // "..."
case '"':
- lexbuf.Reset()
- lexbuf.WriteString(`"<string>"`)
-
- cp = &strbuf
- cp.Reset()
-
- for {
- if l.escchar('"', &escflag, &v) {
- break
- }
- if v < utf8.RuneSelf || escflag != 0 {
- cp.WriteByte(byte(v))
- } else {
- cp.WriteRune(rune(v))
- }
- }
-
- goto strlit
+ l.stdString()
+ return
- // `...`
case '`':
- lexbuf.Reset()
- lexbuf.WriteString("`<string>`")
-
- cp = &strbuf
- cp.Reset()
-
- for {
- c = int(l.getr())
- if c == '\r' {
- continue
- }
- if c == EOF {
- Yyerror("eof in string")
- break
- }
-
- if c == '`' {
- break
- }
- cp.WriteRune(rune(c))
- }
-
- goto strlit
+ l.rawString()
+ return
- // '.'
case '\'':
- if l.escchar('\'', &escflag, &v) {
- Yyerror("empty character literal or unescaped ' in character literal")
- v = '\''
- }
-
- if !l.escchar('\'', &escflag, &v) {
- Yyerror("missing '")
- l.ungetc(int(v))
- }
-
- x := new(Mpint)
- l.val.U = x
- Mpmovecfix(x, v)
- x.Rune = true
- if Debug['x'] != 0 {
- fmt.Printf("lex: codepoint literal\n")
- }
- litbuf = "string literal"
- l.nlsemi = true
- l.tok = LLITERAL
+ l.rune()
return
case '/':
- c1 = l.getc()
+ c1 = l.getr()
if c1 == '*' {
- nl := false
+ c = l.getr()
for {
- c = int(l.getr())
- if c == '\n' {
- nl = true
- }
- for c == '*' {
- c = int(l.getr())
+ if c == '*' {
+ c = l.getr()
if c == '/' {
- if nl {
- l.ungetc('\n')
- }
- goto l0
- }
-
- if c == '\n' {
- nl = true
+ break
}
+ continue
}
-
if c == EOF {
Yyerror("eof in comment")
errorexit()
}
+ c = l.getr()
+ }
+
+ // A comment containing newlines acts like a newline.
+ if lexlineno > lineno && nlsemi {
+ if Debug['x'] != 0 {
+ fmt.Printf("lex: implicit semi\n")
+ }
+ l.tok = ';'
+ return
}
+ goto l0
}
if c1 == '/' {
c = l.getlinepragma()
for {
if c == '\n' || c == EOF {
- l.ungetc(c)
+ l.ungetr(c)
goto l0
}
- c = int(l.getr())
+ c = l.getr()
}
}
- if c1 == '=' {
- op = ODIV
- goto asop
- }
+ op = ODIV
+ prec = PMUL
+ goto binop1
case ':':
- c1 = l.getc()
+ c1 = l.getr()
if c1 == '=' {
- c = int(LCOLAS)
+ c = LCOLAS
goto lx
}
case '*':
- c1 = l.getc()
- if c1 == '=' {
- op = OMUL
- goto asop
- }
+ op = OMUL
+ prec = PMUL
+ goto binop
case '%':
- c1 = l.getc()
- if c1 == '=' {
- op = OMOD
- goto asop
- }
+ op = OMOD
+ prec = PMUL
+ goto binop
case '+':
- c1 = l.getc()
- if c1 == '+' {
- l.nlsemi = true
- c = int(LINC)
- goto lx
- }
-
- if c1 == '=' {
- op = OADD
- goto asop
- }
+ op = OADD
+ goto incop
case '-':
- c1 = l.getc()
- if c1 == '-' {
- l.nlsemi = true
- c = int(LDEC)
- goto lx
- }
-
- if c1 == '=' {
- op = OSUB
- goto asop
- }
+ op = OSUB
+ goto incop
case '>':
- c1 = l.getc()
+ c1 = l.getr()
if c1 == '>' {
- c = int(LRSH)
- c1 = l.getc()
- if c1 == '=' {
- op = ORSH
- goto asop
- }
-
- break
+ c = LRSH
+ op = ORSH
+ prec = PMUL
+ goto binop
}
+ l.prec = PCMP
if c1 == '=' {
- c = int(LGE)
+ c = LGE
+ l.op = OGE
goto lx
}
-
- c = int(LGT)
+ c = LGT
+ l.op = OGT
case '<':
- c1 = l.getc()
+ c1 = l.getr()
if c1 == '<' {
- c = int(LLSH)
- c1 = l.getc()
- if c1 == '=' {
- op = OLSH
- goto asop
- }
-
- break
+ c = LLSH
+ op = OLSH
+ prec = PMUL
+ goto binop
}
- if c1 == '=' {
- c = int(LLE)
+ if c1 == '-' {
+ c = LCOMM
+ // Not a binary operator, but parsed as one
+ // so we can give a good error message when used
+ // in an expression context.
+ l.prec = PCOMM
+ l.op = OSEND
goto lx
}
- if c1 == '-' {
- c = int(LCOMM)
+ l.prec = PCMP
+ if c1 == '=' {
+ c = LLE
+ l.op = OLE
goto lx
}
-
- c = int(LLT)
+ c = LLT
+ l.op = OLT
case '=':
- c1 = l.getc()
+ c1 = l.getr()
if c1 == '=' {
- c = int(LEQ)
+ c = LEQ
+ l.prec = PCMP
+ l.op = OEQ
goto lx
}
case '!':
- c1 = l.getc()
+ c1 = l.getr()
if c1 == '=' {
- c = int(LNE)
+ c = LNE
+ l.prec = PCMP
+ l.op = ONE
goto lx
}
case '&':
- c1 = l.getc()
+ c1 = l.getr()
if c1 == '&' {
- c = int(LANDAND)
+ c = LANDAND
+ l.prec = PANDAND
+ l.op = OANDAND
goto lx
}
if c1 == '^' {
- c = int(LANDNOT)
- c1 = l.getc()
- if c1 == '=' {
- op = OANDNOT
- goto asop
- }
-
- break
+ c = LANDNOT
+ op = OANDNOT
+ prec = PMUL
+ goto binop
}
- if c1 == '=' {
- op = OAND
- goto asop
- }
+ op = OAND
+ prec = PMUL
+ goto binop1
case '|':
- c1 = l.getc()
+ c1 = l.getr()
if c1 == '|' {
- c = int(LOROR)
+ c = LOROR
+ l.prec = POROR
+ l.op = OOROR
goto lx
}
- if c1 == '=' {
- op = OOR
- goto asop
- }
+ op = OOR
+ prec = PADD
+ goto binop1
case '^':
- c1 = l.getc()
- if c1 == '=' {
- op = OXOR
- goto asop
- }
+ op = OXOR
+ prec = PADD
+ goto binop
+
+ case '(', '[', '{', ',', ';':
+ goto lx
case ')', ']', '}':
l.nlsemi = true
goto lx
+ case '#', '$', '?', '@', '\\':
+ if importpkg != nil {
+ goto lx
+ }
+ fallthrough
+
default:
- goto lx
+ // anything else is illegal
+ Yyerror("syntax error: illegal character %#U", c)
+ goto l0
}
- l.ungetc(c1)
+ l.ungetr(c1)
lx:
if Debug['x'] != 0 {
- if c > 0xff {
- fmt.Printf("%v lex: TOKEN %s\n", Ctxt.Line(int(lexlineno)), lexname(c))
+ if c >= utf8.RuneSelf {
+ fmt.Printf("%v lex: TOKEN %s\n", Ctxt.Line(int(lineno)), lexname(c))
} else {
- fmt.Printf("%v lex: TOKEN '%c'\n", Ctxt.Line(int(lexlineno)), c)
+ fmt.Printf("%v lex: TOKEN '%c'\n", Ctxt.Line(int(lineno)), c)
}
}
- if isfrog(c) {
- Yyerror("illegal character 0x%x", uint(c))
- goto l0
- }
- if importpkg == nil && (c == '#' || c == '$' || c == '?' || c == '@' || c == '\\') {
- Yyerror("%s: unexpected %c", "syntax error", c)
- goto l0
+ l.tok = c
+ return
+
+incop:
+ c1 = l.getr()
+ if c1 == c {
+ l.nlsemi = true
+ l.op = op
+ c = LINCOP
+ goto lx
}
+ prec = PADD
+ goto binop1
- l.tok = int32(c)
- return
+binop:
+ c1 = l.getr()
+binop1:
+ if c1 != '=' {
+ l.ungetr(c1)
+ l.op = op
+ l.prec = prec
+ goto lx
+ }
-asop:
l.op = op
if Debug['x'] != 0 {
fmt.Printf("lex: TOKEN ASOP %s=\n", goopnames[op])
}
l.tok = LASOP
- return
+}
+
+func (l *lexer) ident(c rune) {
+ cp := &lexbuf
+ cp.Reset()
- // cp is set to lexbuf and some
- // prefix has been stored
-talph:
+ // accelerate common case (7bit ASCII)
+ for isLetter(c) || isDigit(c) {
+ cp.WriteByte(byte(c))
+ c = l.getr()
+ }
+
+ // general case
for {
if c >= utf8.RuneSelf {
- l.ungetc(c)
- r := rune(l.getr())
-
- // 0xb7 · is used for internal names
- if !unicode.IsLetter(r) && !unicode.IsDigit(r) && (importpkg == nil || r != 0xb7) {
- Yyerror("invalid identifier character U+%04x", r)
- }
- if cp.Len() == 0 && unicode.IsDigit(r) {
- Yyerror("identifier cannot begin with digit U+%04x", r)
+ if unicode.IsLetter(c) || c == '_' || unicode.IsDigit(c) || importpkg != nil && c == 0xb7 {
+ if cp.Len() == 0 && unicode.IsDigit(c) {
+ Yyerror("identifier cannot begin with digit %#U", c)
+ }
+ } else {
+ Yyerror("invalid identifier character %#U", c)
}
- cp.WriteRune(r)
- } else if !isAlnum(c) && c != '_' {
- break
- } else {
+ cp.WriteRune(c)
+ } else if isLetter(c) || isDigit(c) {
cp.WriteByte(byte(c))
+ } else {
+ break
}
- c = l.getc()
+ c = l.getr()
}
cp = nil
- l.ungetc(c)
+ l.ungetr(c)
- s = LookupBytes(lexbuf.Bytes())
- if s.Lexical == LIGNORE {
- goto l0
+ name := lexbuf.Bytes()
+
+ if len(name) >= 2 {
+ if tok, ok := keywords[string(name)]; ok {
+ if Debug['x'] != 0 {
+ fmt.Printf("lex: %s\n", lexname(tok))
+ }
+ switch tok {
+ case LBREAK, LCONTINUE, LFALL, LRETURN:
+ l.nlsemi = true
+ }
+ l.tok = tok
+ return
+ }
}
+ s := LookupBytes(name)
if Debug['x'] != 0 {
- fmt.Printf("lex: %s %s\n", s, lexname(int(s.Lexical)))
+ fmt.Printf("lex: ident %s\n", s)
}
l.sym_ = s
- switch s.Lexical {
- case LNAME, LRETURN, LBREAK, LCONTINUE, LFALL:
- l.nlsemi = true
- }
- l.tok = int32(s.Lexical)
- return
+ l.nlsemi = true
+ l.tok = LNAME
+}
-ncu:
- cp = nil
- l.ungetc(c)
+var keywords = map[string]int32{
+ "break": LBREAK,
+ "case": LCASE,
+ "chan": LCHAN,
+ "const": LCONST,
+ "continue": LCONTINUE,
+ "default": LDEFAULT,
+ "defer": LDEFER,
+ "else": LELSE,
+ "fallthrough": LFALL,
+ "for": LFOR,
+ "func": LFUNC,
+ "go": LGO,
+ "goto": LGOTO,
+ "if": LIF,
+ "import": LIMPORT,
+ "interface": LINTERFACE,
+ "map": LMAP,
+ "package": LPACKAGE,
+ "range": LRANGE,
+ "return": LRETURN,
+ "select": LSELECT,
+ "struct": LSTRUCT,
+ "switch": LSWITCH,
+ "type": LTYPE,
+ "var": LVAR,
+
+ // 💩
+ "notwithstanding": LIGNORE,
+ "thetruthofthematter": LIGNORE,
+ "despiteallobjections": LIGNORE,
+ "whereas": LIGNORE,
+ "insofaras": LIGNORE,
+}
- str = lexbuf.String()
- l.val.U = new(Mpint)
- mpatofix(l.val.U.(*Mpint), str)
- if l.val.U.(*Mpint).Ovf {
- Yyerror("overflow in constant")
- Mpmovecfix(l.val.U.(*Mpint), 0)
- }
+func (l *lexer) number(c rune) {
+ // TODO(gri) this can be done nicely with fewer or even without labels
- if Debug['x'] != 0 {
- fmt.Printf("lex: integer literal\n")
+ var str string
+ cp := &lexbuf
+ cp.Reset()
+
+ if c != '.' {
+ if c != '0' {
+ for isDigit(c) {
+ cp.WriteByte(byte(c))
+ c = l.getr()
+ }
+ if c == '.' {
+ goto casedot
+ }
+ if c == 'e' || c == 'E' || c == 'p' || c == 'P' {
+ goto caseep
+ }
+ if c == 'i' {
+ goto casei
+ }
+ goto ncu
+ }
+
+ // c == 0
+ cp.WriteByte('0')
+ c = l.getr()
+ if c == 'x' || c == 'X' {
+ cp.WriteByte(byte(c))
+ c = l.getr()
+ for isDigit(c) || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
+ cp.WriteByte(byte(c))
+ c = l.getr()
+ }
+ if lexbuf.Len() == 2 {
+ Yyerror("malformed hex constant")
+ }
+ if c == 'p' {
+ goto caseep
+ }
+ goto ncu
+ }
+
+ if c == 'p' { // 0p begins floating point zero
+ goto caseep
+ }
+
+ has8or9 := false
+ for isDigit(c) {
+ if c > '7' {
+ has8or9 = true
+ }
+ cp.WriteByte(byte(c))
+ c = l.getr()
+ }
+ if c == '.' {
+ goto casedot
+ }
+ if c == 'e' || c == 'E' {
+ goto caseep
+ }
+ if c == 'i' {
+ goto casei
+ }
+ if has8or9 {
+ Yyerror("malformed octal constant")
+ }
+ goto ncu
}
- litbuf = "literal " + str
- l.nlsemi = true
- l.tok = LLITERAL
- return
casedot:
- for {
+ // fraction
+ // c == '.'
+ cp.WriteByte('.')
+ c = l.getr()
+ for isDigit(c) {
cp.WriteByte(byte(c))
- c = l.getc()
- if !isDigit(c) {
- break
- }
+ c = l.getr()
}
-
if c == 'i' {
goto casei
}
if c != 'e' && c != 'E' {
goto caseout
}
+ // base-2-exponents (p or P) don't appear in numbers
+ // with fractions - ok to not test for 'p' or 'P'
+ // above
caseep:
+ // exponent
if importpkg == nil && (c == 'p' || c == 'P') {
// <mantissa>p<base-2-exponent> is allowed in .a/.o imports,
// but not in .go sources. See #9036.
Yyerror("malformed floating point constant")
}
cp.WriteByte(byte(c))
- c = l.getc()
+ c = l.getr()
if c == '+' || c == '-' {
cp.WriteByte(byte(c))
- c = l.getc()
+ c = l.getr()
}
if !isDigit(c) {
}
for isDigit(c) {
cp.WriteByte(byte(c))
- c = l.getc()
+ c = l.getr()
}
- if c == 'i' {
- goto casei
+ if c != 'i' {
+ goto caseout
}
- goto caseout
- // imaginary constant
casei:
+ // imaginary constant
cp = nil
str = lexbuf.String()
if Debug['x'] != 0 {
fmt.Printf("lex: imaginary literal\n")
}
- litbuf = "literal " + str
- l.nlsemi = true
- l.tok = LLITERAL
- return
+ goto done
caseout:
cp = nil
- l.ungetc(c)
+ l.ungetr(c)
str = lexbuf.String()
l.val.U = newMpflt()
if Debug['x'] != 0 {
fmt.Printf("lex: floating literal\n")
}
+ goto done
+
+ncu:
+ cp = nil
+ l.ungetr(c)
+
+ str = lexbuf.String()
+ l.val.U = new(Mpint)
+ mpatofix(l.val.U.(*Mpint), str)
+ if l.val.U.(*Mpint).Ovf {
+ Yyerror("overflow in constant")
+ Mpmovecfix(l.val.U.(*Mpint), 0)
+ }
+
+ if Debug['x'] != 0 {
+ fmt.Printf("lex: integer literal\n")
+ }
+
+done:
litbuf = "literal " + str
l.nlsemi = true
l.tok = LLITERAL
- return
+}
+
+func (l *lexer) stdString() {
+ lexbuf.Reset()
+ lexbuf.WriteString(`"<string>"`)
+
+ cp := &strbuf
+ cp.Reset()
+
+ for {
+ r, b, ok := l.onechar('"')
+ if !ok {
+ break
+ }
+ if r == 0 {
+ cp.WriteByte(b)
+ } else {
+ cp.WriteRune(r)
+ }
+ }
+
+ l.val.U = internString(cp.Bytes())
+ if Debug['x'] != 0 {
+ fmt.Printf("lex: string literal\n")
+ }
+ litbuf = "string literal"
+ l.nlsemi = true
+ l.tok = LLITERAL
+}
+
+func (l *lexer) rawString() {
+ lexbuf.Reset()
+ lexbuf.WriteString("`<string>`")
+
+ cp := &strbuf
+ cp.Reset()
+
+ for {
+ c := l.getr()
+ if c == '\r' {
+ continue
+ }
+ if c == EOF {
+ Yyerror("eof in string")
+ break
+ }
+ if c == '`' {
+ break
+ }
+ cp.WriteRune(c)
+ }
-strlit:
l.val.U = internString(cp.Bytes())
if Debug['x'] != 0 {
fmt.Printf("lex: string literal\n")
l.tok = LLITERAL
}
+func (l *lexer) rune() {
+ r, b, ok := l.onechar('\'')
+ if !ok {
+ Yyerror("empty character literal or unescaped ' in character literal")
+ r = '\''
+ }
+ if r == 0 {
+ r = rune(b)
+ }
+
+ if c := l.getr(); c != '\'' {
+ Yyerror("missing '")
+ l.ungetr(c)
+ }
+
+ x := new(Mpint)
+ l.val.U = x
+ Mpmovecfix(x, int64(r))
+ x.Rune = true
+ if Debug['x'] != 0 {
+ fmt.Printf("lex: codepoint literal\n")
+ }
+ litbuf = "rune literal"
+ l.nlsemi = true
+ l.tok = LLITERAL
+}
+
var internedStrings = map[string]string{}
func internString(b []byte) string {
func more(pp *string) bool {
p := *pp
- for p != "" && isSpace(int(p[0])) {
+ for p != "" && isSpace(rune(p[0])) {
p = p[1:]
}
*pp = p
// //line parse.y:15
// as a discontinuity in sequential line numbers.
// the next line of input comes from parse.y:15
-func (l *lexer) getlinepragma() int {
- var cmd, verb, name string
-
- c := int(l.getr())
- if c == 'g' {
+func (l *lexer) getlinepragma() rune {
+ c := l.getr()
+ if c == 'g' { // check for //go: directive
cp := &lexbuf
cp.Reset()
cp.WriteByte('g') // already read
for {
- c = int(l.getr())
+ c = l.getr()
if c == EOF || c >= utf8.RuneSelf {
return c
}
pragcgo(text)
}
- cmd = text
- verb = cmd
- if i := strings.Index(verb, " "); i >= 0 {
+ verb := text
+ if i := strings.Index(text, " "); i >= 0 {
verb = verb[:i]
}
- if verb == "go:linkname" {
+ switch verb {
+ case "go:linkname":
if !imported_unsafe {
Yyerror("//go:linkname only allowed in Go files that import \"unsafe\"")
}
- f := strings.Fields(cmd)
+ f := strings.Fields(text)
if len(f) != 3 {
Yyerror("usage: //go:linkname localname linkname")
- return c
+ break
}
-
Lookup(f[1]).Linkname = f[2]
- return c
- }
-
- if verb == "go:nointerface" && obj.Fieldtrack_enabled != 0 {
- nointerface = true
- return c
- }
-
- if verb == "go:noescape" {
- noescape = true
- return c
- }
-
- if verb == "go:norace" {
- norace = true
- return c
- }
-
- if verb == "go:nosplit" {
- nosplit = true
- return c
- }
-
- if verb == "go:noinline" {
- noinline = true
- return c
- }
-
- if verb == "go:systemstack" {
+ case "go:nointerface":
+ if obj.Fieldtrack_enabled != 0 {
+ l.pragma |= Nointerface
+ }
+ case "go:noescape":
+ l.pragma |= Noescape
+ case "go:norace":
+ l.pragma |= Norace
+ case "go:nosplit":
+ l.pragma |= Nosplit
+ case "go:noinline":
+ l.pragma |= Noinline
+ case "go:systemstack":
if compiling_runtime == 0 {
Yyerror("//go:systemstack only allowed in runtime")
}
- systemstack = true
- return c
- }
-
- if verb == "go:nowritebarrier" {
+ l.pragma |= Systemstack
+ case "go:nowritebarrier":
if compiling_runtime == 0 {
Yyerror("//go:nowritebarrier only allowed in runtime")
}
- nowritebarrier = true
- return c
- }
-
- if verb == "go:nowritebarrierrec" {
+ l.pragma |= Nowritebarrier
+ case "go:nowritebarrierrec":
if compiling_runtime == 0 {
Yyerror("//go:nowritebarrierrec only allowed in runtime")
}
- nowritebarrierrec = true
- nowritebarrier = true // Implies nowritebarrier
- return c
+ l.pragma |= Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier
}
return c
}
+
+ // check for //line directive
if c != 'l' {
return c
}
for i := 1; i < 5; i++ {
- c = int(l.getr())
- if c != int("line "[i]) {
+ c = l.getr()
+ if c != rune("line "[i]) {
return c
}
}
cp.Reset()
linep := 0
for {
- c = int(l.getr())
+ c = l.getr()
if c == EOF {
return c
}
}
cp.WriteByte(byte(c))
}
-
cp = nil
if linep == 0 {
return c
}
text := strings.TrimSuffix(lexbuf.String(), "\r")
- n := 0
- for _, c := range text[linep:] {
- if c < '0' || c > '9' {
- goto out
- }
- n = n*10 + int(c) - '0'
- if n > 1e8 {
- Yyerror("line number out of range")
- errorexit()
- }
+ n, err := strconv.Atoi(text[linep:])
+ if err != nil {
+ return c // todo: make this an error instead? it is almost certainly a bug.
+ }
+ if n > 1e8 {
+ Yyerror("line number out of range")
+ errorexit()
}
-
if n <= 0 {
return c
}
- name = text[:linep-1]
- linehistupdate(name, n)
- return c
-
-out:
+ linehistupdate(text[:linep-1], n)
return c
}
return ""
}
i := 0
- for i < len(p) && !isSpace(int(p[i])) && p[i] != '"' {
+ for i < len(p) && !isSpace(rune(p[i])) && p[i] != '"' {
i++
}
sym := p[:i]
}
}
-func (l *lexer) getc() int {
- c := l.peekc
- if c != 0 {
- l.peekc = l.peekc1
- l.peekc1 = 0
- goto check
+func (l *lexer) getr() rune {
+ // unread rune != 0 available
+ if r := l.peekr1; r != 0 {
+ l.peekr1 = l.peekr2
+ l.peekr2 = 0
+ if r == '\n' && importpkg == nil {
+ lexlineno++
+ }
+ return r
}
-loop:
- c = obj.Bgetc(l.bin)
- // recognize BOM (U+FEFF): UTF-8 encoding is 0xef 0xbb 0xbf
- if c == 0xef {
- buf, err := l.bin.Peek(2)
- if err != nil {
- yyerrorl(int(lexlineno), "illegal UTF-8 sequence ef % x followed by read error (%v)", string(buf), err)
- errorexit()
+redo:
+ // common case: 7bit ASCII
+ c := obj.Bgetc(l.bin)
+ if c < utf8.RuneSelf {
+ if c == 0 {
+ yyerrorl(int(lexlineno), "illegal NUL byte")
+ return 0
}
- if buf[0] == 0xbb && buf[1] == 0xbf {
- yyerrorl(int(lexlineno), "Unicode (UTF-8) BOM in middle of file")
-
- // consume BOM bytes
- obj.Bgetc(l.bin)
- obj.Bgetc(l.bin)
- goto loop
+ if c == '\n' && importpkg == nil {
+ lexlineno++
}
+ return rune(c)
}
+ // c >= utf8.RuneSelf
-check:
- if c == 0 {
- Yyerror("illegal NUL byte")
- return 0
+ // uncommon case: non-ASCII
+ var buf [utf8.UTFMax]byte
+ buf[0] = byte(c)
+ buf[1] = byte(obj.Bgetc(l.bin))
+ i := 2
+ for ; i < len(buf) && !utf8.FullRune(buf[:i]); i++ {
+ buf[i] = byte(obj.Bgetc(l.bin))
}
- if c == '\n' && importpkg == nil {
- lexlineno++
+
+ r, w := utf8.DecodeRune(buf[:i])
+ if r == utf8.RuneError && w == 1 {
+ // The string conversion here makes a copy for passing
+ // to fmt.Printf, so that buf itself does not escape and
+ // can be allocated on the stack.
+ yyerrorl(int(lexlineno), "illegal UTF-8 sequence % x", string(buf[:i]))
}
- return c
-}
-func (l *lexer) ungetc(c int) {
- l.peekc1 = l.peekc
- l.peekc = c
- if c == '\n' && importpkg == nil {
- lexlineno--
+ if r == BOM {
+ yyerrorl(int(lexlineno), "Unicode (UTF-8) BOM in middle of file")
+ goto redo
}
-}
-func (l *lexer) getr() int32 {
- var buf [utf8.UTFMax]byte
+ return r
+}
- for i := 0; ; i++ {
- c := l.getc()
- if i == 0 && c < utf8.RuneSelf {
- return int32(c)
- }
- buf[i] = byte(c)
- if i+1 == len(buf) || utf8.FullRune(buf[:i+1]) {
- r, w := utf8.DecodeRune(buf[:i+1])
- if r == utf8.RuneError && w == 1 {
- lineno = lexlineno
- // The string conversion here makes a copy for passing
- // to fmt.Printf, so that buf itself does not escape and can
- // be allocated on the stack.
- Yyerror("illegal UTF-8 sequence % x", string(buf[:i+1]))
- }
- return int32(r)
- }
+func (l *lexer) ungetr(r rune) {
+ l.peekr2 = l.peekr1
+ l.peekr1 = r
+ if r == '\n' && importpkg == nil {
+ lexlineno--
}
}
-func (l *lexer) escchar(e int, escflg *int, val *int64) bool {
- *escflg = 0
-
- c := int(l.getr())
+// onechar lexes a single character within a rune or interpreted string literal,
+// handling escape sequences as necessary.
+func (l *lexer) onechar(quote rune) (r rune, b byte, ok bool) {
+ c := l.getr()
switch c {
case EOF:
Yyerror("eof in string")
- return true
+ l.ungetr(EOF)
+ return
case '\n':
Yyerror("newline in string")
- return true
+ l.ungetr('\n')
+ return
case '\\':
break
+ case quote:
+ return
+
default:
- if c == e {
- return true
- }
- *val = int64(c)
- return false
+ return c, 0, true
}
- u := 0
- c = int(l.getr())
- var i int
+ c = l.getr()
switch c {
case 'x':
- *escflg = 1 // it's a byte
- i = 2
- goto hex
+ return 0, byte(l.hexchar(2)), true
case 'u':
- i = 4
- u = 1
- goto hex
+ return l.unichar(4), 0, true
case 'U':
- i = 8
- u = 1
- goto hex
-
- case '0',
- '1',
- '2',
- '3',
- '4',
- '5',
- '6',
- '7':
- *escflg = 1 // it's a byte
- x := int64(c) - '0'
+ return l.unichar(8), 0, true
+
+ case '0', '1', '2', '3', '4', '5', '6', '7':
+ x := c - '0'
for i := 2; i > 0; i-- {
- c = l.getc()
+ c = l.getr()
if c >= '0' && c <= '7' {
- x = x*8 + int64(c) - '0'
+ x = x*8 + c - '0'
continue
}
Yyerror("non-octal character in escape sequence: %c", c)
- l.ungetc(c)
+ l.ungetr(c)
}
if x > 255 {
Yyerror("octal escape value > 255: %d", x)
}
- *val = x
- return false
+ return 0, byte(x), true
case 'a':
c = '\a'
c = '\\'
default:
- if c != e {
+ if c != quote {
Yyerror("unknown escape sequence: %c", c)
}
}
- *val = int64(c)
- return false
+ return c, 0, true
+}
-hex:
- x := int64(0)
- for ; i > 0; i-- {
- c = l.getc()
- if c >= '0' && c <= '9' {
- x = x*16 + int64(c) - '0'
- continue
- }
+func (l *lexer) unichar(n int) rune {
+ x := l.hexchar(n)
+ if x > utf8.MaxRune || 0xd800 <= x && x < 0xe000 {
+ Yyerror("invalid Unicode code point in escape sequence: %#x", x)
+ x = utf8.RuneError
+ }
+ return rune(x)
+}
- if c >= 'a' && c <= 'f' {
- x = x*16 + int64(c) - 'a' + 10
- continue
- }
+func (l *lexer) hexchar(n int) uint32 {
+ var x uint32
- if c >= 'A' && c <= 'F' {
- x = x*16 + int64(c) - 'A' + 10
- continue
+ for ; n > 0; n-- {
+ var d uint32
+ switch c := l.getr(); {
+ case isDigit(c):
+ d = uint32(c - '0')
+ case 'a' <= c && c <= 'f':
+ d = uint32(c - 'a' + 10)
+ case 'A' <= c && c <= 'F':
+ d = uint32(c - 'A' + 10)
+ default:
+ Yyerror("non-hex character in escape sequence: %c", c)
+ l.ungetr(c)
+ return x
}
-
- Yyerror("non-hex character in escape sequence: %c", c)
- l.ungetc(c)
- break
+ x = x*16 + d
}
- if u != 0 && (x > utf8.MaxRune || (0xd800 <= x && x < 0xe000)) {
- Yyerror("invalid Unicode code point in escape sequence: %#x", x)
- x = utf8.RuneError
- }
+ return x
+}
- *val = x
- return false
+var basicTypes = [...]struct {
+ name string
+ etype EType
+}{
+ {"int8", TINT8},
+ {"int16", TINT16},
+ {"int32", TINT32},
+ {"int64", TINT64},
+ {"uint8", TUINT8},
+ {"uint16", TUINT16},
+ {"uint32", TUINT32},
+ {"uint64", TUINT64},
+ {"float32", TFLOAT32},
+ {"float64", TFLOAT64},
+ {"complex64", TCOMPLEX64},
+ {"complex128", TCOMPLEX128},
+ {"bool", TBOOL},
+ {"string", TSTRING},
+ {"any", TANY},
}
-var syms = []struct {
- name string
- lexical int
- etype EType
- op Op
+var builtinFuncs = [...]struct {
+ name string
+ op Op
}{
- // basic types
- {"int8", LNAME, TINT8, OXXX},
- {"int16", LNAME, TINT16, OXXX},
- {"int32", LNAME, TINT32, OXXX},
- {"int64", LNAME, TINT64, OXXX},
- {"uint8", LNAME, TUINT8, OXXX},
- {"uint16", LNAME, TUINT16, OXXX},
- {"uint32", LNAME, TUINT32, OXXX},
- {"uint64", LNAME, TUINT64, OXXX},
- {"float32", LNAME, TFLOAT32, OXXX},
- {"float64", LNAME, TFLOAT64, OXXX},
- {"complex64", LNAME, TCOMPLEX64, OXXX},
- {"complex128", LNAME, TCOMPLEX128, OXXX},
- {"bool", LNAME, TBOOL, OXXX},
- {"string", LNAME, TSTRING, OXXX},
- {"any", LNAME, TANY, OXXX},
- {"break", LBREAK, Txxx, OXXX},
- {"case", LCASE, Txxx, OXXX},
- {"chan", LCHAN, Txxx, OXXX},
- {"const", LCONST, Txxx, OXXX},
- {"continue", LCONTINUE, Txxx, OXXX},
- {"default", LDEFAULT, Txxx, OXXX},
- {"else", LELSE, Txxx, OXXX},
- {"defer", LDEFER, Txxx, OXXX},
- {"fallthrough", LFALL, Txxx, OXXX},
- {"for", LFOR, Txxx, OXXX},
- {"func", LFUNC, Txxx, OXXX},
- {"go", LGO, Txxx, OXXX},
- {"goto", LGOTO, Txxx, OXXX},
- {"if", LIF, Txxx, OXXX},
- {"import", LIMPORT, Txxx, OXXX},
- {"interface", LINTERFACE, Txxx, OXXX},
- {"map", LMAP, Txxx, OXXX},
- {"package", LPACKAGE, Txxx, OXXX},
- {"range", LRANGE, Txxx, OXXX},
- {"return", LRETURN, Txxx, OXXX},
- {"select", LSELECT, Txxx, OXXX},
- {"struct", LSTRUCT, Txxx, OXXX},
- {"switch", LSWITCH, Txxx, OXXX},
- {"type", LTYPE, Txxx, OXXX},
- {"var", LVAR, Txxx, OXXX},
- {"append", LNAME, Txxx, OAPPEND},
- {"cap", LNAME, Txxx, OCAP},
- {"close", LNAME, Txxx, OCLOSE},
- {"complex", LNAME, Txxx, OCOMPLEX},
- {"copy", LNAME, Txxx, OCOPY},
- {"delete", LNAME, Txxx, ODELETE},
- {"imag", LNAME, Txxx, OIMAG},
- {"len", LNAME, Txxx, OLEN},
- {"make", LNAME, Txxx, OMAKE},
- {"new", LNAME, Txxx, ONEW},
- {"panic", LNAME, Txxx, OPANIC},
- {"print", LNAME, Txxx, OPRINT},
- {"println", LNAME, Txxx, OPRINTN},
- {"real", LNAME, Txxx, OREAL},
- {"recover", LNAME, Txxx, ORECOVER},
- {"notwithstanding", LIGNORE, Txxx, OXXX},
- {"thetruthofthematter", LIGNORE, Txxx, OXXX},
- {"despiteallobjections", LIGNORE, Txxx, OXXX},
- {"whereas", LIGNORE, Txxx, OXXX},
- {"insofaras", LIGNORE, Txxx, OXXX},
+ {"append", OAPPEND},
+ {"cap", OCAP},
+ {"close", OCLOSE},
+ {"complex", OCOMPLEX},
+ {"copy", OCOPY},
+ {"delete", ODELETE},
+ {"imag", OIMAG},
+ {"len", OLEN},
+ {"make", OMAKE},
+ {"new", ONEW},
+ {"panic", OPANIC},
+ {"print", OPRINT},
+ {"println", OPRINTN},
+ {"real", OREAL},
+ {"recover", ORECOVER},
}
// lexinit initializes known symbols and the basic types.
func lexinit() {
- for _, s := range syms {
- lex := s.lexical
- s1 := Lookup(s.name)
- s1.Lexical = uint16(lex)
-
- if etype := s.etype; etype != Txxx {
- if int(etype) >= len(Types) {
- Fatalf("lexinit: %s bad etype", s.name)
+ for _, s := range basicTypes {
+ etype := s.etype
+ if int(etype) >= len(Types) {
+ Fatalf("lexinit: %s bad etype", s.name)
+ }
+ s2 := Pkglookup(s.name, builtinpkg)
+ t := Types[etype]
+ if t == nil {
+ t = typ(etype)
+ t.Sym = s2
+ if etype != TANY && etype != TSTRING {
+ dowidth(t)
}
- s2 := Pkglookup(s.name, builtinpkg)
- t := Types[etype]
- if t == nil {
- t = typ(etype)
- t.Sym = s2
-
- if etype != TANY && etype != TSTRING {
- dowidth(t)
- }
- Types[etype] = t
- }
-
- s2.Lexical = LNAME
- s2.Def = typenod(t)
- s2.Def.Name = new(Name)
- continue
+ Types[etype] = t
}
+ s2.Def = typenod(t)
+ s2.Def.Name = new(Name)
+ }
+ for _, s := range builtinFuncs {
// TODO(marvin): Fix Node.EType type union.
- if etype := s.op; etype != OXXX {
- s2 := Pkglookup(s.name, builtinpkg)
- s2.Lexical = LNAME
- s2.Def = Nod(ONAME, nil, nil)
- s2.Def.Sym = s2
- s2.Def.Etype = EType(etype)
- }
+ s2 := Pkglookup(s.name, builtinpkg)
+ s2.Def = Nod(ONAME, nil, nil)
+ s2.Def.Sym = s2
+ s2.Def.Etype = EType(s.op)
}
// logically, the type of a string literal.
s.Def = nodlit(v)
s.Def.Sym = s
s.Def.Name = new(Name)
+
+ s = Pkglookup("iota", builtinpkg)
+ s.Def = Nod(OIOTA, nil, nil)
+ s.Def.Sym = s
+ s.Def.Name = new(Name)
}
func lexinit1() {
t.Type.Type = f
// error type
- s := Lookup("error")
-
- s.Lexical = LNAME
- s1 := Pkglookup("error", builtinpkg)
+ s := Pkglookup("error", builtinpkg)
errortype = t
- errortype.Sym = s1
- s1.Lexical = LNAME
- s1.Def = typenod(errortype)
+ errortype.Sym = s
+ s.Def = typenod(errortype)
// byte alias
- s = Lookup("byte")
-
- s.Lexical = LNAME
- s1 = Pkglookup("byte", builtinpkg)
+ s = Pkglookup("byte", builtinpkg)
bytetype = typ(TUINT8)
- bytetype.Sym = s1
- s1.Lexical = LNAME
- s1.Def = typenod(bytetype)
- s1.Def.Name = new(Name)
+ bytetype.Sym = s
+ s.Def = typenod(bytetype)
+ s.Def.Name = new(Name)
// rune alias
- s = Lookup("rune")
-
- s.Lexical = LNAME
- s1 = Pkglookup("rune", builtinpkg)
+ s = Pkglookup("rune", builtinpkg)
runetype = typ(TINT32)
- runetype.Sym = s1
- s1.Lexical = LNAME
- s1.Def = typenod(runetype)
- s1.Def.Name = new(Name)
-}
-
-func lexfini() {
- for i := range syms {
- lex := syms[i].lexical
- if lex != LNAME {
- continue
- }
- s := Lookup(syms[i].name)
- s.Lexical = uint16(lex)
-
- etype := syms[i].etype
- if etype != Txxx && (etype != TANY || Debug['A'] != 0) && s.Def == nil {
- s.Def = typenod(Types[etype])
- s.Def.Name = new(Name)
- s.Origpkg = builtinpkg
- }
-
- // TODO(marvin): Fix Node.EType type union.
- etype = EType(syms[i].op)
- if etype != EType(OXXX) && s.Def == nil {
- s.Def = Nod(ONAME, nil, nil)
- s.Def.Sym = s
- s.Def.Etype = etype
- s.Origpkg = builtinpkg
- }
- }
+ runetype.Sym = s
+ s.Def = typenod(runetype)
+ s.Def.Name = new(Name)
// backend-specific builtin types (e.g. int).
for i := range Thearch.Typedefs {
- s := Lookup(Thearch.Typedefs[i].Name)
- if s.Def == nil {
- s.Def = typenod(Types[Thearch.Typedefs[i].Etype])
- s.Def.Name = new(Name)
- s.Origpkg = builtinpkg
- }
- }
-
- // there's only so much table-driven we can handle.
- // these are special cases.
- if s := Lookup("byte"); s.Def == nil {
- s.Def = typenod(bytetype)
- s.Def.Name = new(Name)
- s.Origpkg = builtinpkg
- }
-
- if s := Lookup("error"); s.Def == nil {
- s.Def = typenod(errortype)
- s.Def.Name = new(Name)
- s.Origpkg = builtinpkg
- }
-
- if s := Lookup("rune"); s.Def == nil {
- s.Def = typenod(runetype)
- s.Def.Name = new(Name)
- s.Origpkg = builtinpkg
- }
-
- if s := Lookup("nil"); s.Def == nil {
- var v Val
- v.U = new(NilVal)
- s.Def = nodlit(v)
- s.Def.Sym = s
+ s := Pkglookup(Thearch.Typedefs[i].Name, builtinpkg)
+ s.Def = typenod(Types[Thearch.Typedefs[i].Etype])
s.Def.Name = new(Name)
s.Origpkg = builtinpkg
}
+}
- if s := Lookup("iota"); s.Def == nil {
- s.Def = Nod(OIOTA, nil, nil)
- s.Def.Sym = s
- s.Origpkg = builtinpkg
- }
-
- if s := Lookup("true"); s.Def == nil {
- s.Def = Nodbool(true)
- s.Def.Sym = s
- s.Def.Name = new(Name)
- s.Origpkg = builtinpkg
- }
+func lexfini() {
+ for _, s := range builtinpkg.Syms {
+ if s.Def == nil {
+ continue
+ }
+ s1 := Lookup(s.Name)
+ if s1.Def != nil {
+ continue
+ }
- if s := Lookup("false"); s.Def == nil {
- s.Def = Nodbool(false)
- s.Def.Sym = s
- s.Def.Name = new(Name)
- s.Origpkg = builtinpkg
+ s1.Def = s.Def
+ s1.Block = s.Block
}
nodfp = Nod(ONAME, nil, nil)
nodfp.Sym = Lookup(".fp")
}
-var lexn = map[int]string{
+var lexn = map[rune]string{
LANDAND: "ANDAND",
LANDNOT: "ANDNOT",
LASOP: "ASOP",
LCONST: "CONST",
LCONTINUE: "CONTINUE",
LDDD: "...",
- LDEC: "DEC",
LDEFAULT: "DEFAULT",
LDEFER: "DEFER",
LELSE: "ELSE",
LGT: "GT",
LIF: "IF",
LIMPORT: "IMPORT",
- LINC: "INC",
+ LINCOP: "INCOP",
LINTERFACE: "INTERFACE",
LLE: "LE",
LLITERAL: "LITERAL",
LVAR: "VAR",
}
-func lexname(lex int) string {
+func lexname(lex rune) string {
if s, ok := lexn[lex]; ok {
return s
}
s := Mpgetfix(b)
if s < 0 || s >= Mpprec {
- Yyerror("stupid shift: %d", s)
+ msg := "shift count too large"
+ if s < 0 {
+ msg = "invalid negative shift count"
+ }
+ Yyerror("%s: %d", msg, s)
Mpmovecfix(a, 0)
return
}
s := Mpgetfix(b)
if s < 0 {
- Yyerror("stupid shift: %d", s)
+ Yyerror("invalid negative shift count: %d", s)
if a.Val.Sign() < 0 {
Mpmovecfix(a, -1)
} else {
"math"
)
-/// implements float arihmetic
+// implements float arithmetic
func newMpflt() *Mpflt {
var a Mpflt
// Ordermapassign appends n to order->out, introducing temporaries
// to make sure that all map assignments have the form m[k] = x,
-// where x is adressable.
+// where x is addressable.
// (Orderexpr has already been called on n, so we know k is addressable.)
//
// If n is m[k] = x where x is not addressable, the rewrite is:
}
case OCLOSURE:
- if n.Noescape && n.Func.Cvars != nil {
+ if n.Noescape && len(n.Func.Cvars.Slice()) > 0 {
prealloc[n] = ordertemp(Types[TUINT8], order, false) // walk will fill in correct type
}
}
case LASOP:
tok = goopnames[p.op] + "="
+ case LINCOP:
+ tok = goopnames[p.op] + goopnames[p.op]
default:
tok = tokstring(p.tok)
}
}
// Like syntax_error, but reports error at given line rather than current lexer line.
-func (p *parser) syntax_error_at(lineno int32, msg string) {
- defer func(lineno int32) {
- lexlineno = lineno
- }(lexlineno)
- lexlineno = lineno
+func (p *parser) syntax_error_at(lno int32, msg string) {
+ defer func(lno int32) {
+ lineno = lno
+ }(lineno)
+ lineno = lno
p.syntax_error(msg)
}
LANDAND: "&&",
LANDNOT: "&^",
LCOMM: "<-",
- LDEC: "--",
LEQ: "==",
LGE: ">=",
LGT: ">",
LIGNORE: "LIGNORE", // we should never see this one
- LINC: "++",
+ LINCOP: "opop",
LLE: "<=",
LLSH: "<<",
LLT: "<",
defer p.trace("package_")()
}
- if p.got(LPACKAGE) {
- mkpackage(p.sym().Name)
- } else {
- prevlineno = lineno // see issue #13267
+ if !p.got(LPACKAGE) {
p.syntax_error("package statement must be first")
errorexit()
}
+ mkpackage(p.sym().Name)
}
// ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
}
line := int32(parserline())
- path := p.val
- p.next()
- importfile(&path, p.indent)
- if importpkg == nil {
+ // We need to clear importpkg before calling p.next(),
+ // otherwise it will affect lexlineno.
+ // TODO(mdempsky): Fix this clumsy API.
+ importfile(&p.val, p.indent)
+ ipkg := importpkg
+ importpkg = nil
+
+ p.next()
+ if ipkg == nil {
if nerrors == 0 {
Fatalf("phase error in import")
}
return
}
- ipkg := importpkg
- importpkg = nil
-
ipkg.Direct = true
if my == nil {
stmt.Etype = EType(op) // rathole to pass opcode
return stmt
- case LINC:
- // expr LINC
- p.next()
-
- stmt := Nod(OASOP, lhs, Nodintconst(1))
- stmt.Implicit = true
- stmt.Etype = EType(OADD)
- return stmt
-
- case LDEC:
- // expr LDEC
+ case LINCOP:
+ // expr LINCOP
p.next()
stmt := Nod(OASOP, lhs, Nodintconst(1))
stmt.Implicit = true
- stmt.Etype = EType(OSUB)
+ stmt.Etype = EType(p.op)
return stmt
case ':':
ls = p.stmt()
if ls == missing_stmt {
// report error at line of ':' token
- p.syntax_error_at(prevlineno, "missing statement after label")
+ p.syntax_error_at(label.Lineno, "missing statement after label")
// we are already at the end of the labeled statement - no need to advance
return missing_stmt
}
return hdr
}
-// TODO(gri) should have lexer return this info - no need for separate lookup
-// (issue 13244)
-var prectab = map[int32]struct {
- prec int // > 0 (0 indicates not found)
- op Op
-}{
- // not an expression anymore, but left in so we can give a good error
- // message when used in expression context
- LCOMM: {1, OSEND},
-
- LOROR: {2, OOROR},
-
- LANDAND: {3, OANDAND},
-
- LEQ: {4, OEQ},
- LNE: {4, ONE},
- LLE: {4, OLE},
- LGE: {4, OGE},
- LLT: {4, OLT},
- LGT: {4, OGT},
-
- '+': {5, OADD},
- '-': {5, OSUB},
- '|': {5, OOR},
- '^': {5, OXOR},
-
- '*': {6, OMUL},
- '/': {6, ODIV},
- '%': {6, OMOD},
- '&': {6, OAND},
- LLSH: {6, OLSH},
- LRSH: {6, ORSH},
- LANDNOT: {6, OANDNOT},
-}
-
// Expression = UnaryExpr | Expression binary_op Expression .
-func (p *parser) bexpr(prec int) *Node {
+func (p *parser) bexpr(prec OpPrec) *Node {
// don't trace bexpr - only leads to overly nested trace output
+ // prec is precedence of the prior/enclosing binary operator (if any),
+ // so we only want to parse tokens of greater precedence.
+
x := p.uexpr()
- t := prectab[p.tok]
- for tprec := t.prec; tprec >= prec; tprec-- {
- for tprec == prec {
- p.next()
- y := p.bexpr(t.prec + 1)
- x = Nod(t.op, x, y)
- t = prectab[p.tok]
- tprec = t.prec
- }
+ for p.prec > prec {
+ op, prec1 := p.op, p.prec
+ p.next()
+ x = Nod(op, x, p.bexpr(prec1))
}
return x
}
defer p.trace("expr")()
}
- return p.bexpr(1)
+ return p.bexpr(0)
}
func unparen(x *Node) *Node {
return nil
}
-func (p *parser) dcl_name(sym *Sym) *Node {
+func (p *parser) dcl_name() *Node {
if trace && Debug['x'] != 0 {
defer p.trace("dcl_name")()
}
+ symlineno := lineno
+ sym := p.sym()
if sym == nil {
- yyerrorl(int(prevlineno), "invalid declaration")
+ yyerrorl(int(symlineno), "invalid declaration")
return nil
}
return dclname(sym)
}
p.want(LFUNC)
- f := p.fndcl()
+ f := p.fndcl(p.pragma&Nointerface != 0)
body := p.fnbody()
if f == nil {
return nil
}
- if noescape && body != nil {
- Yyerror("can only use //go:noescape with external func implementations")
- }
f.Nbody = body
+ f.Noescape = p.pragma&Noescape != 0
+ if f.Noescape && body != nil {
+ Yyerror("can only use //go:noescape with external func implementations")
+ }
+ f.Func.Pragma = p.pragma
f.Func.Endlineno = lineno
- f.Noescape = noescape
- f.Func.Norace = norace
- f.Func.Nosplit = nosplit
- f.Func.Noinline = noinline
- f.Func.Nowritebarrier = nowritebarrier
- f.Func.Nowritebarrierrec = nowritebarrierrec
- f.Func.Systemstack = systemstack
+
funcbody(f)
return f
// Function = Signature FunctionBody .
// MethodDecl = "func" Receiver MethodName ( Function | Signature ) .
// Receiver = Parameters .
-func (p *parser) fndcl() *Node {
+func (p *parser) fndcl(nointerface bool) *Node {
if trace && Debug['x'] != 0 {
defer p.trace("fndcl")()
}
ss.Type = functype(s2.N, s6, s8)
checkwidth(ss.Type)
- addmethod(s4, ss.Type, false, nointerface)
- nointerface = false
+ addmethod(s4, ss.Type, false, false)
funchdr(ss)
// inl.C's inlnode in on a dotmeth node expects to find the inlineable body as
testdclstack()
}
- noescape = false
- noinline = false
- nointerface = false
- norace = false
- nosplit = false
- nowritebarrier = false
- nowritebarrierrec = false
- systemstack = false
+ // Reset p.pragma BEFORE advancing to the next token (consuming ';')
+ // since comments before may set pragmas for the next function decl.
+ p.pragma = 0
- // Consume ';' AFTER resetting the above flags since
- // it may read the subsequent comment line which may
- // set the flags for the next function declaration.
if p.tok != EOF && !p.got(';') {
p.syntax_error("after top level declaration")
p.advance(LVAR, LCONST, LTYPE, LFUNC)
stmt := Nod(ORETURN, nil, nil)
stmt.List = results
if stmt.List == nil && Curfn != nil {
- for l := Curfn.Func.Dcl; l != nil; l = l.Next {
- if l.N.Class == PPARAM {
+ for _, ln := range Curfn.Func.Dcl {
+ if ln.Class == PPARAM {
continue
}
- if l.N.Class != PPARAMOUT {
+ if ln.Class != PPARAMOUT {
break
}
- if l.N.Sym.Def != l.N {
- Yyerror("%s is shadowed during return", l.N.Sym.Name)
+ if ln.Sym.Def != ln {
+ Yyerror("%s is shadowed during return", ln.Sym.Name)
}
}
}
defer p.trace("dcl_name_list")()
}
- l := list1(p.dcl_name(p.sym()))
+ l := list1(p.dcl_name())
for p.got(',') {
- l = list(l, p.dcl_name(p.sym()))
+ l = list(l, p.dcl_name())
}
return l
}
"cmd/internal/obj"
"crypto/md5"
"fmt"
+ "sort"
"strings"
)
return a.Sym.Name < b.Sym.Name
}
+// byStackvar implements sort.Interface for []*Node using cmpstackvarlt.
+type byStackVar []*Node
+
+func (s byStackVar) Len() int { return len(s) }
+func (s byStackVar) Less(i, j int) bool { return cmpstackvarlt(s[i], s[j]) }
+func (s byStackVar) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
// stkdelta records the stack offset delta for a node
// during the compaction of the stack frame to remove
// unused stack slots.
Stksize = 0
stkptrsize = 0
- if Curfn.Func.Dcl == nil {
+ if len(Curfn.Func.Dcl) == 0 {
return
}
// Mark the PAUTO's unused.
- for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
- if ll.N.Class == PAUTO {
- ll.N.Used = false
+ for _, ln := range Curfn.Func.Dcl {
+ if ln.Class == PAUTO {
+ ln.Used = false
}
}
markautoused(ptxt)
- listsort(&Curfn.Func.Dcl, cmpstackvarlt)
+ sort.Sort(byStackVar(Curfn.Func.Dcl))
// Unused autos are at the end, chop 'em off.
- ll := Curfn.Func.Dcl
-
- n := ll.N
+ n := Curfn.Func.Dcl[0]
if n.Class == PAUTO && n.Op == ONAME && !n.Used {
// No locals used at all
Curfn.Func.Dcl = nil
return
}
- for ll := Curfn.Func.Dcl; ll.Next != nil; ll = ll.Next {
- n = ll.Next.N
+ for i := 1; i < len(Curfn.Func.Dcl); i++ {
+ n = Curfn.Func.Dcl[i]
if n.Class == PAUTO && n.Op == ONAME && !n.Used {
- ll.Next = nil
- Curfn.Func.Dcl.End = ll
+ Curfn.Func.Dcl = Curfn.Func.Dcl[:i]
break
}
}
// Reassign stack offsets of the locals that are still there.
var w int64
- for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
- n = ll.N
+ for _, n := range Curfn.Func.Dcl {
if n.Class != PAUTO || n.Op != ONAME {
continue
}
fixautoused(ptxt)
// The debug information needs accurate offsets on the symbols.
- for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
- if ll.N.Class != PAUTO || ll.N.Op != ONAME {
+ for _, ln := range Curfn.Func.Dcl {
+ if ln.Class != PAUTO || ln.Op != ONAME {
continue
}
- ll.N.Xoffset += stkdelta[ll.N]
- delete(stkdelta, ll.N)
+ ln.Xoffset += stkdelta[ln]
+ delete(stkdelta, ln)
}
}
if fn.Func.Needctxt {
ptxt.From3.Offset |= obj.NEEDCTXT
}
- if fn.Func.Nosplit {
+ if fn.Func.Pragma&Nosplit != 0 {
ptxt.From3.Offset |= obj.NOSPLIT
}
- if fn.Func.Systemstack {
+ if fn.Func.Pragma&Systemstack != 0 {
ptxt.From.Sym.Cfunc = 1
}
gtrack(tracksym(t))
}
- for l := fn.Func.Dcl; l != nil; l = l.Next {
- n = l.N
+ for _, n := range fn.Func.Dcl {
if n.Op != ONAME { // might be OTYPE or OLITERAL
continue
}
switch n.Class {
case PAUTO, PPARAM, PPARAMOUT:
- Nodconst(&nod1, Types[TUINTPTR], l.N.Type.Width)
- p = Thearch.Gins(obj.ATYPE, l.N, &nod1)
- p.From.Gotype = Linksym(ngotype(l.N))
+ Nodconst(&nod1, Types[TUINTPTR], n.Type.Width)
+ p = Thearch.Gins(obj.ATYPE, n, &nod1)
+ p.From.Gotype = Linksym(ngotype(n))
}
}
ssafn.Free()
return
}
- Genlist(Curfn.Func.Enter)
+ Genslice(Curfn.Func.Enter.Slice())
Genlist(Curfn.Nbody)
gclean()
checklabels()
}
// Inserts prev before curr in the instruction
-// stream. Any control flow, such as branches or fall throughs, that target the
+// stream. Any control flow, such as branches or fall-throughs, that target the
// existing instruction are adjusted to target the new instruction.
func splicebefore(lv *Liveness, bb *BasicBlock, prev *obj.Prog, curr *obj.Prog) {
// There may be other instructions pointing at curr,
// variables.
func getvariables(fn *Node) []*Node {
result := make([]*Node, 0, 0)
- for ll := fn.Func.Dcl; ll != nil; ll = ll.Next {
- if ll.N.Op == ONAME {
+ for _, ln := range fn.Func.Dcl {
+ if ln.Op == ONAME {
// In order for GODEBUG=gcdead=1 to work, each bitmap needs
// to contain information about all variables covered by the bitmap.
// For local variables, the bitmap only covers the stkptrsize
// Later, when we want to find the index of a node in the variables list,
// we will check that n->curfn == curfn and n->opt > 0. Then n->opt - 1
// is the index in the variables list.
- ll.N.SetOpt(nil)
+ ln.SetOpt(nil)
// The compiler doesn't emit initializations for zero-width parameters or results.
- if ll.N.Type.Width == 0 {
+ if ln.Type.Width == 0 {
continue
}
- ll.N.Name.Curfn = Curfn
- switch ll.N.Class {
+ ln.Name.Curfn = Curfn
+ switch ln.Class {
case PAUTO:
- if haspointers(ll.N.Type) {
- ll.N.SetOpt(int32(len(result)))
- result = append(result, ll.N)
+ if haspointers(ln.Type) {
+ ln.SetOpt(int32(len(result)))
+ result = append(result, ln)
}
case PPARAM, PPARAMOUT:
- ll.N.SetOpt(int32(len(result)))
- result = append(result, ll.N)
+ ln.SetOpt(int32(len(result)))
+ result = append(result, ln)
}
}
}
}
func checkauto(fn *Node, p *obj.Prog, n *Node) {
- for l := fn.Func.Dcl; l != nil; l = l.Next {
- if l.N.Op == ONAME && l.N.Class == PAUTO && l.N == n {
+ for _, ln := range fn.Func.Dcl {
+ if ln.Op == ONAME && ln.Class == PAUTO && ln == n {
return
}
}
}
fmt.Printf("checkauto %v: %v (%p; class=%d) not found in %p %v\n", funcSym(Curfn), n, n, n.Class, p, p)
- for l := fn.Func.Dcl; l != nil; l = l.Next {
- fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class)
+ for _, ln := range fn.Func.Dcl {
+ fmt.Printf("\t%v (%p; class=%d)\n", ln, ln, ln.Class)
}
Yyerror("checkauto: invariant lost")
}
if isfunny(n) {
return
}
- var a *Node
var class Class
- for l := fn.Func.Dcl; l != nil; l = l.Next {
- a = l.N
+ for _, a := range fn.Func.Dcl {
class = a.Class &^ PHEAP
if a.Op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n {
return
}
fmt.Printf("checkparam %v: %v (%p; class=%d) not found in %v\n", Curfn, n, n, n.Class, p)
- for l := fn.Func.Dcl; l != nil; l = l.Next {
- fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class)
+ for _, ln := range fn.Func.Dcl {
+ fmt.Printf("\t%v (%p; class=%d)\n", ln, ln, ln.Class)
}
Yyerror("checkparam: invariant lost")
}
onebitwritesymbol(lv.argslivepointers, argssym)
// Free everything.
- for l := fn.Func.Dcl; l != nil; l = l.Next {
- if l.N != nil {
- l.N.SetOpt(nil)
+ for _, ln := range fn.Func.Dcl {
+ if ln != nil {
+ ln.SetOpt(nil)
}
}
freeliveness(lv)
fmt.Printf("%v\n", p)
}
if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil && p.To.Val.(*obj.Prog).As == obj.AJMP {
- p.To.Val = chasejmp(p.To.Val.(*obj.Prog), &jmploop)
- if Debug['R'] != 0 && Debug['v'] != 0 {
- fmt.Printf("->%v\n", p)
+ if Debug['N'] == 0 {
+ p.To.Val = chasejmp(p.To.Val.(*obj.Prog), &jmploop)
+ if Debug['R'] != 0 && Debug['v'] != 0 {
+ fmt.Printf("->%v\n", p)
+ }
}
}
p.Opt = dead
}
-
if Debug['R'] != 0 && Debug['v'] != 0 {
fmt.Printf("\n")
}
// pass 4: elide JMP to next instruction.
// only safe if there are no jumps to JMPs anymore.
- if jmploop == 0 {
+ if jmploop == 0 && Debug['N'] == 0 {
var last *obj.Prog
for p := firstp; p != nil; p = p.Link {
if p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH && p.To.Val == p.Link {
// Control flow analysis. The Flow structures hold predecessor and successor
// information as well as basic loop analysis.
//
-// graph = flowstart(firstp, 0);
+// graph = Flowstart(firstp, nil)
// ... use flow graph ...
-// flowend(graph); // free graph
+// Flowend(graph) // free graph
//
// Typical uses of the flow graph are to iterate over all the flow-relevant instructions:
//
-// for(f = graph->start; f != nil; f = f->link)
+// for f := graph.Start; f != nil; f = f.Link {}
//
// or, given an instruction f, to iterate over all the predecessors, which is
-// f->p1 and this list:
+// f.P1 and this list:
//
-// for(f2 = f->p2; f2 != nil; f2 = f2->p2link)
+// for f2 := f.P2; f2 != nil; f2 = f2.P2link {}
//
-// The size argument to flowstart specifies an amount of zeroed memory
-// to allocate in every f->data field, for use by the client.
-// If size == 0, f->data will be nil.
+// The second argument (newData) to Flowstart specifies a func to create object
+// for every f.Data field, for use by the client.
+// If newData is nil, f.Data will be nil.
var flowmark int
me = r1.Rpo
d = -1
- // rpo2r[r->rpo] == r protects against considering dead code,
- // which has r->rpo == 0.
+ // rpo2r[r.Rpo] == r protects against considering dead code,
+ // which has r.Rpo == 0.
if r1.P1 != nil && rpo2r[r1.P1.Rpo] == r1.P1 && r1.P1.Rpo < me {
d = r1.P1.Rpo
}
// Build list of all mergeable variables.
var vars []*TempVar
- for l := Curfn.Func.Dcl; l != nil; l = l.Next {
- if n := l.N; canmerge(n) {
+ for _, n := range Curfn.Func.Dcl {
+ if canmerge(n) {
v := &TempVar{}
vars = append(vars, v)
n.SetOpt(v)
// Traverse live range of each variable to set start, end.
// Each flood uses a new value of gen so that we don't have
- // to clear all the r->active words after each variable.
+ // to clear all the r.Active words after each variable.
gen := uint32(0)
for _, v := range vars {
}
// Delete merged nodes from declaration list.
- for lp := &Curfn.Func.Dcl; ; {
- l := *lp
- if l == nil {
- break
- }
-
- Curfn.Func.Dcl.End = l
- n := l.N
+ dcl := make([]*Node, 0, len(Curfn.Func.Dcl)-nkill)
+ for _, n := range Curfn.Func.Dcl {
v, _ := n.Opt().(*TempVar)
if v != nil && (v.merge != nil || v.removed) {
- *lp = l.Next
continue
}
-
- lp = &l.Next
+ dcl = append(dcl, n)
}
+ Curfn.Func.Dcl = dcl
// Clear aux structures.
for _, v := range vars {
// from memory without being rechecked. Other variables need to be checked on
// each load.
-var killed int // f->data is either nil or &killed
+var killed int // f.Data is either nil or &killed
func nilopt(firstp *obj.Prog) {
g := Flowstart(firstp, nil)
}
func instrument(fn *Node) {
- if ispkgin(omit_pkgs) || fn.Func.Norace {
+ if ispkgin(omit_pkgs) || fn.Func.Pragma&Norace != 0 {
return
}
instrumentlist(fn.Nbody, nil)
// nothing interesting for race detector in fn->enter
- instrumentlist(fn.Func.Exit, nil)
+ instrumentslice(fn.Func.Exit.Slice(), nil)
}
if flag_race != 0 {
nodpc.Type = Types[TUINTPTR]
nodpc.Xoffset = int64(-Widthptr)
nd := mkcall("racefuncenter", nil, nil, nodpc)
- fn.Func.Enter = concat(list1(nd), fn.Func.Enter)
+ fn.Func.Enter.Set(append([]*Node{nd}, fn.Func.Enter.Slice()...))
nd = mkcall("racefuncexit", nil, nil)
- fn.Func.Exit = list(fn.Func.Exit, nd)
+ fn.Func.Exit.Append(nd)
}
if Debug['W'] != 0 {
s := fmt.Sprintf("after instrument %v", fn.Func.Nname.Sym)
dumplist(s, fn.Nbody)
s = fmt.Sprintf("enter %v", fn.Func.Nname.Sym)
- dumplist(s, fn.Func.Enter)
+ dumpslice(s, fn.Func.Enter.Slice())
s = fmt.Sprintf("exit %v", fn.Func.Nname.Sym)
- dumplist(s, fn.Func.Exit)
+ dumpslice(s, fn.Func.Exit.Slice())
}
}
}
}
+func instrumentslice(l []*Node, init **NodeList) {
+ for i := range l {
+ var instr *NodeList
+ instrumentnode(&l[i], &instr, 0, 0)
+ if init == nil {
+ l[i].Ninit = concat(l[i].Ninit, instr)
+ } else {
+ *init = concat(*init, instr)
+ }
+ }
+}
+
// walkexpr and walkstmt combined
// walks the tree and adds calls to the
// instrumentation code to top-level (statement) nodes' init
ot := off
s := sym
- if t.Sym != nil {
- ot = dgostringptr(s, ot, t.Sym.Name)
- if t != Types[t.Etype] && t != errortype {
- ot = dgopkgpath(s, ot, t.Sym.Pkg)
- } else {
- ot = dgostringptr(s, ot, "")
- }
+ if t.Sym != nil && t != Types[t.Etype] && t != errortype {
+ ot = dgopkgpath(s, ot, t.Sym.Pkg)
} else {
ot = dgostringptr(s, ot, "")
- ot = dgostringptr(s, ot, "")
}
// slice header
algsym = dalgsym(t)
}
- var sptr *Sym
tptr := Ptrto(t)
if !Isptr[t.Etype] && (t.Sym != nil || methods(tptr) != nil) {
- sptr = dtypesym(tptr)
- } else {
- sptr = weaktypesym(tptr)
+ sptr := dtypesym(tptr)
+ r := obj.Addrel(Linksym(s))
+ r.Off = 0
+ r.Siz = 0
+ r.Sym = sptr.Lsym
+ r.Type = obj.R_USETYPE
}
gcsym, useGCProg, ptrdata := dgcsym(t)
// gcdata *byte
// string *string
// *uncommonType
- // ptrToThis *rtype
// }
ot = duintptr(s, ot, uint64(t.Width))
ot = duintptr(s, ot, uint64(ptrdata))
} else {
ot = dsymptr(s, ot, algsym, 0)
}
- ot = dsymptr(s, ot, gcsym, 0)
+ ot = dsymptr(s, ot, gcsym, 0) // gcdata
p := Tconv(t, obj.FmtLeft|obj.FmtUnsigned)
- //print("dcommontype: %s\n", p);
- ot = dgostringptr(s, ot, p) // string
+ _, symdata := stringsym(p) // string
+ ot = dsymptr(s, ot, symdata, 0)
+ ot = duintxx(s, ot, uint64(len(p)), Widthint)
+ //fmt.Printf("dcommontype: %s\n", p)
// skip pointer to extraType,
// which follows the rest of this type structure.
// otherwise linker will assume 0.
ot += Widthptr
- ot = dsymptr(s, ot, sptr, 0) // ptrto type
return ot
}
switch t.Etype {
default:
ot = dcommontype(s, ot, t)
- xt = ot - 2*Widthptr
+ xt = ot - 1*Widthptr
case TARRAY:
if t.Bound >= 0 {
t2.Bound = -1 // slice
s2 := dtypesym(t2)
ot = dcommontype(s, ot, t)
- xt = ot - 2*Widthptr
+ xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0)
ot = dsymptr(s, ot, s2, 0)
ot = duintptr(s, ot, uint64(t.Bound))
s1 := dtypesym(t.Type)
ot = dcommontype(s, ot, t)
- xt = ot - 2*Widthptr
+ xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0)
}
s1 := dtypesym(t.Type)
ot = dcommontype(s, ot, t)
- xt = ot - 2*Widthptr
+ xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0)
ot = duintptr(s, ot, uint64(t.Chan))
}
ot = dcommontype(s, ot, t)
- xt = ot - 2*Widthptr
+ xt = ot - 1*Widthptr
ot = duint8(s, ot, uint8(obj.Bool2int(isddd)))
// two slice headers: in and out.
// ../../../../runtime/type.go:/interfaceType
ot = dcommontype(s, ot, t)
- xt = ot - 2*Widthptr
+ xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint)
ot = duintxx(s, ot, uint64(n), Widthint)
ot = duintxx(s, ot, uint64(n), Widthint)
s3 := dtypesym(mapbucket(t))
s4 := dtypesym(hmap(t))
ot = dcommontype(s, ot, t)
- xt = ot - 2*Widthptr
+ xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0)
ot = dsymptr(s, ot, s2, 0)
ot = dsymptr(s, ot, s3, 0)
s1 := dtypesym(t.Type)
ot = dcommontype(s, ot, t)
- xt = ot - 2*Widthptr
+ xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s1, 0)
// ../../../../runtime/type.go:/structType
}
ot = dcommontype(s, ot, t)
- xt = ot - 2*Widthptr
+ xt = ot - 1*Widthptr
ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint)
ot = duintxx(s, ot, uint64(n), Widthint)
ot = duintxx(s, ot, uint64(n), Widthint)
// we want be able to find.
if t.Sym == nil {
switch t.Etype {
- case TPTR32, TPTR64:
- // The ptrto field of the type data cannot be relied on when
- // dynamic linking: a type T may be defined in a module that makes
- // no use of pointers to that type, but another module can contain
- // a package that imports the first one and does use *T pointers.
- // The second module will end up defining type data for *T and a
- // type.*T symbol pointing at it. It's important that calling
- // .PtrTo() on the reflect.Type for T returns this type data and
- // not some synthesized object, so we need reflect to be able to
- // find it!
- if !Ctxt.Flag_dynlink {
- break
- }
- fallthrough
- case TARRAY, TCHAN, TFUNC, TMAP:
+ case TPTR32, TPTR64, TARRAY, TCHAN, TFUNC, TMAP:
slink := typelinksym(t)
dsymptr(slink, 0, s, 0)
ggloblsym(slink, int32(Widthptr), int16(dupok|obj.RODATA))
// be multiples of four words. On 32-bit systems that's 16 bytes, and
// all size classes >= 16 bytes are 16-byte aligned, so no real constraint.
// On 64-bit systems, that's 32 bytes, and 32-byte alignment is guaranteed
-// for size classes >= 256 bytes. On a 64-bit sytem, 256 bytes allocated
+// for size classes >= 256 bytes. On a 64-bit system, 256 bytes allocated
// is 32 pointers, the bits for which fit in 4 bytes. So maxPtrmaskBytes
// must be >= 4.
//
break
case OCLOSURE:
- if r.Func.Cvars == nil {
+ if len(r.Func.Cvars.Slice()) == 0 {
// Closures with no captured variables are globals,
// so the assignment can be done at link time.
n := *l
printssa := strings.HasSuffix(name, "_ssa") || strings.Contains(name, "_ssa.") || name == os.Getenv("GOSSAFUNC")
if printssa {
fmt.Println("generating SSA for", name)
- dumplist("buildssa-enter", fn.Func.Enter)
+ dumpslice("buildssa-enter", fn.Func.Enter.Slice())
dumplist("buildssa-body", fn.Nbody)
- dumplist("buildssa-exit", fn.Func.Exit)
+ dumpslice("buildssa-exit", fn.Func.Exit.Slice())
}
var s state
// Generate addresses of local declarations
s.decladdrs = map[*Node]*ssa.Value{}
- for d := fn.Func.Dcl; d != nil; d = d.Next {
- n := d.N
+ for _, n := range fn.Func.Dcl {
switch n.Class {
case PPARAM:
aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n})
}
// Convert the AST-based IR to the SSA-based IR
- s.stmtList(fn.Func.Enter)
+ s.stmts(fn.Func.Enter)
s.stmtList(fn.Nbody)
// fallthrough to exit
if s.curBlock != nil {
- s.stmtList(s.exitCode)
+ s.stmts(s.exitCode)
m := s.mem()
b := s.endBlock()
b.Kind = ssa.BlockRet
s.linkForwardReferences()
// Don't carry reference this around longer than necessary
- s.exitCode = nil
+ s.exitCode = Nodes{}
// Main call to ssa package to compile function
ssa.Compile(s.f)
fwdGotos []*Node
// Code that must precede any return
// (e.g., copying value of heap-escaped paramout back to true paramout)
- exitCode *NodeList
+ exitCode Nodes
// unlabeled break and continue statement tracking
breakTo *ssa.Block // current target for plain break statement
return s.constInt32(t, int32(c))
}
+func (s *state) stmts(a Nodes) {
+ for _, x := range a.Slice() {
+ s.stmt(x)
+ }
+}
+
// ssaStmtList converts the statement n to SSA and adds it to s.
func (s *state) stmtList(l *NodeList) {
for ; l != nil; l = l.Next {
case ORETURN:
s.stmtList(n.List)
- s.stmtList(s.exitCode)
+ s.stmts(s.exitCode)
m := s.mem()
b := s.endBlock()
b.Kind = ssa.BlockRet
b.Control = m
case ORETJMP:
s.stmtList(n.List)
- s.stmtList(s.exitCode)
+ s.stmts(s.exitCode)
m := s.mem()
b := s.endBlock()
b.Kind = ssa.BlockRetJmp
type Error struct {
lineno int
- seq int
msg string
}
func adderr(line int, format string, args ...interface{}) {
errors = append(errors, Error{
- seq: len(errors),
lineno: line,
msg: fmt.Sprintf("%v: %s\n", Ctxt.Line(line), fmt.Sprintf(format, args...)),
})
}
-// errcmp sorts errors by line, then seq, then message.
-type errcmp []Error
+// byLineno sorts errors by lineno.
+type byLineno []Error
-func (x errcmp) Len() int { return len(x) }
-func (x errcmp) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-func (x errcmp) Less(i, j int) bool {
- a := &x[i]
- b := &x[j]
- if a.lineno != b.lineno {
- return a.lineno < b.lineno
- }
- if a.seq != b.seq {
- return a.seq < b.seq
- }
- return a.msg < b.msg
-}
+func (x byLineno) Len() int { return len(x) }
+func (x byLineno) Less(i, j int) bool { return x[i].lineno < x[j].lineno }
+func (x byLineno) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func Flusherrors() {
bstdout.Flush()
if len(errors) == 0 {
return
}
- sort.Sort(errcmp(errors))
+ sort.Stable(byLineno(errors))
for i := 0; i < len(errors); i++ {
if i == 0 || errors[i].msg != errors[i-1].msg {
fmt.Printf("%s", errors[i].msg)
}
}
-var yyerror_lastsyntax int
+var yyerror_lastsyntax int32
func Yyerror(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
nsyntaxerrors++
// only one syntax error per line
- if int32(yyerror_lastsyntax) == lexlineno {
+ if yyerror_lastsyntax == lineno {
return
}
- yyerror_lastsyntax = int(lexlineno)
+ yyerror_lastsyntax = lineno
- // plain "syntax error" gets "near foo" added
- if msg == "syntax error" {
- yyerrorl(int(lexlineno), "syntax error near %s", lexbuf.String())
- return
- }
-
- yyerrorl(int(lexlineno), "%s", msg)
+ yyerrorl(int(lineno), "%s", msg)
return
}
}
s := &Sym{
- Name: name,
- Pkg: pkg,
- Lexical: LNAME,
+ Name: name,
+ Pkg: pkg,
}
if name == "init" {
initSyms = append(initSyms, s)
n.Orig = norig
}
-// ispaddedfield reports whether the given field
-// is followed by padding. For the case where t is
-// the last field, total gives the size of the enclosing struct.
-func ispaddedfield(t *Type, total int64) bool {
- if t.Etype != TFIELD {
- Fatalf("ispaddedfield called non-field %v", t)
- }
- if t.Down == nil {
- return t.Width+t.Type.Width != total
- }
- return t.Width+t.Type.Width != t.Down.Width
-}
-
-func algtype1(t *Type, bad **Type) int {
- if bad != nil {
- *bad = nil
- }
- if t.Broke {
- return AMEM
- }
- if t.Noalg {
- return ANOEQ
- }
-
- switch t.Etype {
- // will be defined later.
- case TANY, TFORW:
- *bad = t
-
- return -1
-
- case TINT8,
- TUINT8,
- TINT16,
- TUINT16,
- TINT32,
- TUINT32,
- TINT64,
- TUINT64,
- TINT,
- TUINT,
- TUINTPTR,
- TBOOL,
- TPTR32,
- TPTR64,
- TCHAN,
- TUNSAFEPTR:
- return AMEM
-
- case TFUNC, TMAP:
- if bad != nil {
- *bad = t
- }
- return ANOEQ
-
- case TFLOAT32:
- return AFLOAT32
-
- case TFLOAT64:
- return AFLOAT64
-
- case TCOMPLEX64:
- return ACPLX64
-
- case TCOMPLEX128:
- return ACPLX128
-
- case TSTRING:
- return ASTRING
-
- case TINTER:
- if isnilinter(t) {
- return ANILINTER
- }
- return AINTER
-
- case TARRAY:
- if Isslice(t) {
- if bad != nil {
- *bad = t
- }
- return ANOEQ
- }
-
- a := algtype1(t.Type, bad)
- if a == ANOEQ || a == AMEM {
- if a == ANOEQ && bad != nil {
- *bad = t
- }
- return a
- }
-
- switch t.Bound {
- case 0:
- // We checked above that the element type is comparable.
- return AMEM
- case 1:
- // Single-element array is same as its lone element.
- return a
- }
-
- return -1 // needs special compare
-
- case TSTRUCT:
- if t.Type != nil && t.Type.Down == nil && !isblanksym(t.Type.Sym) {
- // One-field struct is same as that one field alone.
- return algtype1(t.Type.Type, bad)
- }
-
- ret := AMEM
- var a int
- for t1 := t.Type; t1 != nil; t1 = t1.Down {
- // All fields must be comparable.
- a = algtype1(t1.Type, bad)
-
- if a == ANOEQ {
- return ANOEQ
- }
-
- // Blank fields, padded fields, fields with non-memory
- // equality need special compare.
- if a != AMEM || isblanksym(t1.Sym) || ispaddedfield(t1, t.Width) {
- ret = -1
- continue
- }
- }
-
- return ret
- }
-
- Fatalf("algtype1: unexpected type %v", t)
- return 0
-}
-
-func algtype(t *Type) int {
- a := algtype1(t, nil)
- if a == AMEM {
- switch t.Width {
- case 0:
- return AMEM0
- case 1:
- return AMEM8
- case 2:
- return AMEM16
- case 4:
- return AMEM32
- case 8:
- return AMEM64
- case 16:
- return AMEM128
- }
- }
-
- return a
-}
-
func maptype(key *Type, val *Type) *Type {
if key != nil {
var bad *Type
if Curfn != nil {
fmt.Printf("--- %v frame ---\n", Curfn.Func.Nname.Sym)
- for l := Curfn.Func.Dcl; l != nil; l = l.Next {
- printframenode(l.N)
+ for _, ln := range Curfn.Func.Dcl {
+ printframenode(ln)
}
}
}
return n
}
-func hashfor(t *Type) *Node {
- var sym *Sym
-
- a := algtype1(t, nil)
- switch a {
- case AMEM:
- Fatalf("hashfor with AMEM type")
-
- case AINTER:
- sym = Pkglookup("interhash", Runtimepkg)
-
- case ANILINTER:
- sym = Pkglookup("nilinterhash", Runtimepkg)
-
- case ASTRING:
- sym = Pkglookup("strhash", Runtimepkg)
-
- case AFLOAT32:
- sym = Pkglookup("f32hash", Runtimepkg)
-
- case AFLOAT64:
- sym = Pkglookup("f64hash", Runtimepkg)
-
- case ACPLX64:
- sym = Pkglookup("c64hash", Runtimepkg)
-
- case ACPLX128:
- sym = Pkglookup("c128hash", Runtimepkg)
-
- default:
- sym = typesymprefix(".hash", t)
- }
-
- n := newname(sym)
- n.Class = PFUNC
- tfn := Nod(OTFUNC, nil, nil)
- tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
- tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
- tfn.Rlist = list(tfn.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
- typecheck(&tfn, Etype)
- n.Type = tfn.Type
- return n
-}
-
-// Generate a helper function to compute the hash of a value of type t.
-func genhash(sym *Sym, t *Type) {
- if Debug['r'] != 0 {
- fmt.Printf("genhash %v %v\n", sym, t)
- }
-
- lineno = 1 // less confusing than end of input
- dclcontext = PEXTERN
- markdcl()
-
- // func sym(p *T, h uintptr) uintptr
- fn := Nod(ODCLFUNC, nil, nil)
-
- fn.Func.Nname = newname(sym)
- fn.Func.Nname.Class = PFUNC
- tfn := Nod(OTFUNC, nil, nil)
- fn.Func.Nname.Name.Param.Ntype = tfn
-
- n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
- tfn.List = list(tfn.List, n)
- np := n.Left
- n = Nod(ODCLFIELD, newname(Lookup("h")), typenod(Types[TUINTPTR]))
- tfn.List = list(tfn.List, n)
- nh := n.Left
- n = Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])) // return value
- tfn.Rlist = list(tfn.Rlist, n)
-
- funchdr(fn)
- typecheck(&fn.Func.Nname.Name.Param.Ntype, Etype)
-
- // genhash is only called for types that have equality but
- // cannot be handled by the standard algorithms,
- // so t must be either an array or a struct.
- switch t.Etype {
- default:
- Fatalf("genhash %v", t)
-
- case TARRAY:
- if Isslice(t) {
- Fatalf("genhash %v", t)
- }
-
- // An array of pure memory would be handled by the
- // standard algorithm, so the element type must not be
- // pure memory.
- hashel := hashfor(t.Type)
-
- n := Nod(ORANGE, nil, Nod(OIND, np, nil))
- ni := newname(Lookup("i"))
- ni.Type = Types[TINT]
- n.List = list1(ni)
- n.Colas = true
- colasdefn(n.List, n)
- ni = n.List.N
-
- // h = hashel(&p[i], h)
- call := Nod(OCALL, hashel, nil)
-
- nx := Nod(OINDEX, np, ni)
- nx.Bounded = true
- na := Nod(OADDR, nx, nil)
- na.Etype = 1 // no escape to heap
- call.List = list(call.List, na)
- call.List = list(call.List, nh)
- n.Nbody = list(n.Nbody, Nod(OAS, nh, call))
-
- fn.Nbody = list(fn.Nbody, n)
-
- // Walk the struct using memhash for runs of AMEM
- // and calling specific hash functions for the others.
- case TSTRUCT:
- var first *Type
-
- offend := int64(0)
- var size int64
- var call *Node
- var nx *Node
- var na *Node
- var hashel *Node
- for t1 := t.Type; ; t1 = t1.Down {
- if t1 != nil && algtype1(t1.Type, nil) == AMEM && !isblanksym(t1.Sym) {
- offend = t1.Width + t1.Type.Width
- if first == nil {
- first = t1
- }
-
- // If it's a memory field but it's padded, stop here.
- if ispaddedfield(t1, t.Width) {
- t1 = t1.Down
- } else {
- continue
- }
- }
-
- // Run memhash for fields up to this one.
- if first != nil {
- size = offend - first.Width // first->width is offset
- hashel = hashmem(first.Type)
-
- // h = hashel(&p.first, size, h)
- call = Nod(OCALL, hashel, nil)
-
- nx = Nod(OXDOT, np, newname(first.Sym)) // TODO: fields from other packages?
- na = Nod(OADDR, nx, nil)
- na.Etype = 1 // no escape to heap
- call.List = list(call.List, na)
- call.List = list(call.List, nh)
- call.List = list(call.List, Nodintconst(size))
- fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
-
- first = nil
- }
-
- if t1 == nil {
- break
- }
- if isblanksym(t1.Sym) {
- continue
- }
-
- // Run hash for this field.
- if algtype1(t1.Type, nil) == AMEM {
- hashel = hashmem(t1.Type)
-
- // h = memhash(&p.t1, h, size)
- call = Nod(OCALL, hashel, nil)
-
- nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages?
- na = Nod(OADDR, nx, nil)
- na.Etype = 1 // no escape to heap
- call.List = list(call.List, na)
- call.List = list(call.List, nh)
- call.List = list(call.List, Nodintconst(t1.Type.Width))
- fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
- } else {
- hashel = hashfor(t1.Type)
-
- // h = hashel(&p.t1, h)
- call = Nod(OCALL, hashel, nil)
-
- nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages?
- na = Nod(OADDR, nx, nil)
- na.Etype = 1 // no escape to heap
- call.List = list(call.List, na)
- call.List = list(call.List, nh)
- fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
- }
- }
- }
-
- r := Nod(ORETURN, nil, nil)
- r.List = list(r.List, nh)
- fn.Nbody = list(fn.Nbody, r)
-
- if Debug['r'] != 0 {
- dumplist("genhash body", fn.Nbody)
- }
-
- funcbody(fn)
- Curfn = fn
- fn.Func.Dupok = true
- typecheck(&fn, Etop)
- typechecklist(fn.Nbody, Etop)
- Curfn = nil
-
- // Disable safemode while compiling this code: the code we
- // generate internally can refer to unsafe.Pointer.
- // In this case it can happen if we need to generate an ==
- // for a struct containing a reflect.Value, which itself has
- // an unexported field of type unsafe.Pointer.
- old_safemode := safemode
-
- safemode = 0
- funccompile(fn)
- safemode = old_safemode
-}
-
-// eqfield returns the node
-// p.field == q.field
-func eqfield(p *Node, q *Node, field *Node) *Node {
- nx := Nod(OXDOT, p, field)
- ny := Nod(OXDOT, q, field)
- ne := Nod(OEQ, nx, ny)
- return ne
-}
-
-func eqmemfunc(size int64, type_ *Type, needsize *int) *Node {
- var fn *Node
-
- switch size {
- default:
- fn = syslook("memequal", 1)
- *needsize = 1
-
- case 1, 2, 4, 8, 16:
- buf := fmt.Sprintf("memequal%d", int(size)*8)
- fn = syslook(buf, 1)
- *needsize = 0
- }
-
- substArgTypes(fn, type_, type_)
- return fn
-}
-
-// eqmem returns the node
-// memequal(&p.field, &q.field [, size])
-func eqmem(p *Node, q *Node, field *Node, size int64) *Node {
- var needsize int
-
- nx := Nod(OADDR, Nod(OXDOT, p, field), nil)
- nx.Etype = 1 // does not escape
- ny := Nod(OADDR, Nod(OXDOT, q, field), nil)
- ny.Etype = 1 // does not escape
- typecheck(&nx, Erv)
- typecheck(&ny, Erv)
-
- call := Nod(OCALL, eqmemfunc(size, nx.Type.Type, &needsize), nil)
- call.List = list(call.List, nx)
- call.List = list(call.List, ny)
- if needsize != 0 {
- call.List = list(call.List, Nodintconst(size))
- }
-
- return call
-}
-
-// geneq generates a helper function to
-// check equality of two values of type t.
-func geneq(sym *Sym, t *Type) {
- if Debug['r'] != 0 {
- fmt.Printf("geneq %v %v\n", sym, t)
- }
-
- lineno = 1 // less confusing than end of input
- dclcontext = PEXTERN
- markdcl()
-
- // func sym(p, q *T) bool
- fn := Nod(ODCLFUNC, nil, nil)
-
- fn.Func.Nname = newname(sym)
- fn.Func.Nname.Class = PFUNC
- tfn := Nod(OTFUNC, nil, nil)
- fn.Func.Nname.Name.Param.Ntype = tfn
-
- n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
- tfn.List = list(tfn.List, n)
- np := n.Left
- n = Nod(ODCLFIELD, newname(Lookup("q")), typenod(Ptrto(t)))
- tfn.List = list(tfn.List, n)
- nq := n.Left
- n = Nod(ODCLFIELD, nil, typenod(Types[TBOOL]))
- tfn.Rlist = list(tfn.Rlist, n)
-
- funchdr(fn)
-
- // geneq is only called for types that have equality but
- // cannot be handled by the standard algorithms,
- // so t must be either an array or a struct.
- switch t.Etype {
- default:
- Fatalf("geneq %v", t)
-
- case TARRAY:
- if Isslice(t) {
- Fatalf("geneq %v", t)
- }
-
- // An array of pure memory would be handled by the
- // standard memequal, so the element type must not be
- // pure memory. Even if we unrolled the range loop,
- // each iteration would be a function call, so don't bother
- // unrolling.
- nrange := Nod(ORANGE, nil, Nod(OIND, np, nil))
-
- ni := newname(Lookup("i"))
- ni.Type = Types[TINT]
- nrange.List = list1(ni)
- nrange.Colas = true
- colasdefn(nrange.List, nrange)
- ni = nrange.List.N
-
- // if p[i] != q[i] { return false }
- nx := Nod(OINDEX, np, ni)
-
- nx.Bounded = true
- ny := Nod(OINDEX, nq, ni)
- ny.Bounded = true
-
- nif := Nod(OIF, nil, nil)
- nif.Left = Nod(ONE, nx, ny)
- r := Nod(ORETURN, nil, nil)
- r.List = list(r.List, Nodbool(false))
- nif.Nbody = list(nif.Nbody, r)
- nrange.Nbody = list(nrange.Nbody, nif)
- fn.Nbody = list(fn.Nbody, nrange)
-
- // return true
- ret := Nod(ORETURN, nil, nil)
- ret.List = list(ret.List, Nodbool(true))
- fn.Nbody = list(fn.Nbody, ret)
-
- // Walk the struct using memequal for runs of AMEM
- // and calling specific equality tests for the others.
- // Skip blank-named fields.
- case TSTRUCT:
- var first *Type
-
- var conjuncts []*Node
- offend := int64(0)
- var size int64
- for t1 := t.Type; ; t1 = t1.Down {
- if t1 != nil && algtype1(t1.Type, nil) == AMEM && !isblanksym(t1.Sym) {
- offend = t1.Width + t1.Type.Width
- if first == nil {
- first = t1
- }
-
- // If it's a memory field but it's padded, stop here.
- if ispaddedfield(t1, t.Width) {
- t1 = t1.Down
- } else {
- continue
- }
- }
-
- // Run memequal for fields up to this one.
- // TODO(rsc): All the calls to newname are wrong for
- // cross-package unexported fields.
- if first != nil {
- if first.Down == t1 {
- conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
- } else if first.Down.Down == t1 {
- conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
- first = first.Down
- if !isblanksym(first.Sym) {
- conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym)))
- }
- } else {
- // More than two fields: use memequal.
- size = offend - first.Width // first->width is offset
- conjuncts = append(conjuncts, eqmem(np, nq, newname(first.Sym), size))
- }
-
- first = nil
- }
-
- if t1 == nil {
- break
- }
- if isblanksym(t1.Sym) {
- continue
- }
-
- // Check this field, which is not just memory.
- conjuncts = append(conjuncts, eqfield(np, nq, newname(t1.Sym)))
- }
-
- var and *Node
- switch len(conjuncts) {
- case 0:
- and = Nodbool(true)
- case 1:
- and = conjuncts[0]
- default:
- and = Nod(OANDAND, conjuncts[0], conjuncts[1])
- for _, conjunct := range conjuncts[2:] {
- and = Nod(OANDAND, and, conjunct)
- }
- }
-
- ret := Nod(ORETURN, nil, nil)
- ret.List = list(ret.List, and)
- fn.Nbody = list(fn.Nbody, ret)
- }
-
- if Debug['r'] != 0 {
- dumplist("geneq body", fn.Nbody)
- }
-
- funcbody(fn)
- Curfn = fn
- fn.Func.Dupok = true
- typecheck(&fn, Etop)
- typechecklist(fn.Nbody, Etop)
- Curfn = nil
-
- // Disable safemode while compiling this code: the code we
- // generate internally can refer to unsafe.Pointer.
- // In this case it can happen if we need to generate an ==
- // for a struct containing a reflect.Value, which itself has
- // an unexported field of type unsafe.Pointer.
- old_safemode := safemode
- safemode = 0
-
- // Disable checknils while compiling this code.
- // We are comparing a struct or an array,
- // neither of which can be nil, and our comparisons
- // are shallow.
- Disable_checknil++
-
- funccompile(fn)
-
- safemode = old_safemode
- Disable_checknil--
-}
-
func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase int) *Type {
*followptr = false
// Func holds Node fields used only with function-like nodes.
type Func struct {
Shortname *Node
- Enter *NodeList // for example, allocate and initialize memory for escaping parameters
- Exit *NodeList
- Cvars *NodeList // closure params
- Dcl *NodeList // autodcl for this func/closure
- Inldcl *NodeList // copy of dcl for use in inlining
+ Enter Nodes // for example, allocate and initialize memory for escaping parameters
+ Exit Nodes
+ Cvars Nodes // closure params
+ Dcl []*Node // autodcl for this func/closure
+ Inldcl *[]*Node // copy of dcl for use in inlining
Closgen int
Outerfunc *Node
Fieldtrack []*Type
Depth int32
Endlineno int32
+ WBLineno int32 // line number of first write barrier
- Norace bool // func must not have race detector annotations
- Nosplit bool // func should not execute on separate stack
- Noinline bool // func should not be inlined
- Nowritebarrier bool // emit compiler error instead of write barrier
- Nowritebarrierrec bool // error on write barrier in this or recursive callees
- Dupok bool // duplicate definitions ok
- Wrapper bool // is method wrapper
- Needctxt bool // function uses context register (has closure variables)
- Systemstack bool // must run on system stack
-
- WBLineno int32 // line number of first write barrier
+ Pragma Pragma // go:xxx function annotations
+ Dupok bool // duplicate definitions ok
+ Wrapper bool // is method wrapper
+ Needctxt bool // function uses context register (has closure variables)
}
type Op uint8
}
return int(n)
}
+
+// Nodes is a pointer to a slice of *Node.
+// For fields that are not used in most nodes, this is used instead of
+// a slice to save space.
+type Nodes struct{ slice *[]*Node }
+
+// Slice returns the entries in Nodes as a slice.
+// Changes to the slice entries (as in s[i] = n) will be reflected in
+// the Nodes.
+func (n *Nodes) Slice() []*Node {
+ if n.slice == nil {
+ return nil
+ }
+ return *n.slice
+}
+
+// NodeList returns the entries in Nodes as a NodeList.
+// Changes to the NodeList entries (as in l.N = n) will *not* be
+// reflect in the Nodes.
+// This wastes memory and should be used as little as possible.
+func (n *Nodes) NodeList() *NodeList {
+ if n.slice == nil {
+ return nil
+ }
+ var ret *NodeList
+ for _, n := range *n.slice {
+ ret = list(ret, n)
+ }
+ return ret
+}
+
+// Set sets Nodes to a slice.
+// This takes ownership of the slice.
+func (n *Nodes) Set(s []*Node) {
+ if len(s) == 0 {
+ n.slice = nil
+ } else {
+ n.slice = &s
+ }
+}
+
+// Append appends entries to Nodes.
+// If a slice is passed in, this will take ownership of it.
+func (n *Nodes) Append(a ...*Node) {
+ if n.slice == nil {
+ if len(a) > 0 {
+ n.slice = &a
+ }
+ } else {
+ *n.slice = append(*n.slice, a...)
+ }
+}
}
}
+func typecheckslice(l []*Node, top int) {
+ for i := range l {
+ typecheck(&l[i], top)
+ }
+}
+
var _typekind = []string{
TINT: "int",
TUINT: "uint",
addmethod(n.Func.Shortname.Sym, t, true, n.Func.Nname.Nointerface)
}
- for l := n.Func.Dcl; l != nil; l = l.Next {
- if l.N.Op == ONAME && (l.N.Class == PPARAM || l.N.Class == PPARAMOUT) {
- l.N.Name.Decldepth = 1
+ for _, ln := range n.Func.Dcl {
+ if ln.Op == ONAME && (ln.Class == PPARAM || ln.Class == PPARAMOUT) {
+ ln.Name.Decldepth = 1
}
}
}
// Final typecheck for any unused variables.
// It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below.
- for l := fn.Func.Dcl; l != nil; l = l.Next {
- if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO {
- typecheck(&l.N, Erv|Easgn)
+ for i, ln := range fn.Func.Dcl {
+ if ln.Op == ONAME && ln.Class&^PHEAP == PAUTO {
+ typecheck(&ln, Erv|Easgn)
+ fn.Func.Dcl[i] = ln
}
}
// Propagate the used flag for typeswitch variables up to the NONAME in it's definition.
- for l := fn.Func.Dcl; l != nil; l = l.Next {
- if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO && l.N.Name.Defn != nil && l.N.Name.Defn.Op == OTYPESW && l.N.Used {
- l.N.Name.Defn.Left.Used = true
+ for _, ln := range fn.Func.Dcl {
+ if ln.Op == ONAME && ln.Class&^PHEAP == PAUTO && ln.Name.Defn != nil && ln.Name.Defn.Op == OTYPESW && ln.Used {
+ ln.Name.Defn.Left.Used = true
}
}
- for l := fn.Func.Dcl; l != nil; l = l.Next {
- if l.N.Op != ONAME || l.N.Class&^PHEAP != PAUTO || l.N.Sym.Name[0] == '&' || l.N.Used {
+ for _, ln := range fn.Func.Dcl {
+ if ln.Op != ONAME || ln.Class&^PHEAP != PAUTO || ln.Sym.Name[0] == '&' || ln.Used {
continue
}
- if defn := l.N.Name.Defn; defn != nil && defn.Op == OTYPESW {
+ if defn := ln.Name.Defn; defn != nil && defn.Op == OTYPESW {
if defn.Left.Used {
continue
}
lineno = defn.Left.Lineno
- Yyerror("%v declared and not used", l.N.Sym)
+ Yyerror("%v declared and not used", ln.Sym)
defn.Left.Used = true // suppress repeats
} else {
- lineno = l.N.Lineno
- Yyerror("%v declared and not used", l.N.Sym)
+ lineno = ln.Lineno
+ Yyerror("%v declared and not used", ln.Sym)
}
}
}
heapmoves()
- if Debug['W'] != 0 && Curfn.Func.Enter != nil {
+ if Debug['W'] != 0 && len(Curfn.Func.Enter.Slice()) > 0 {
s := fmt.Sprintf("enter %v", Curfn.Func.Nname.Sym)
- dumplist(s, Curfn.Func.Enter)
+ dumpslice(s, Curfn.Func.Enter.Slice())
}
}
}
}
+func walkstmtslice(l []*Node) {
+ for i := range l {
+ walkstmt(&l[i])
+ }
+}
+
func samelist(a *NodeList, b *NodeList) bool {
for ; a != nil && b != nil; a, b = a.Next, b.Next {
if a.N != b.N {
}
func paramoutheap(fn *Node) bool {
- for l := fn.Func.Dcl; l != nil; l = l.Next {
- switch l.N.Class {
+ for _, ln := range fn.Func.Dcl {
+ switch ln.Class {
case PPARAMOUT,
PPARAMOUT | PHEAP:
- return l.N.Addrtaken
+ return ln.Addrtaken
// stop early - parameters are over
case PAUTO,
var rl *NodeList
var cl Class
- for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
- cl = ll.N.Class &^ PHEAP
+ for _, ln := range Curfn.Func.Dcl {
+ cl = ln.Class &^ PHEAP
if cl == PAUTO {
break
}
if cl == PPARAMOUT {
- rl = list(rl, ll.N)
+ rl = list(rl, ln)
}
}
ll := ascompatee(n.Op, rl, n.List, &n.Ninit)
n.List = reorder3(ll)
for lr := n.List; lr != nil; lr = lr.Next {
- lr.N = applywritebarrier(lr.N, &n.Ninit)
+ lr.N = applywritebarrier(lr.N)
}
break
}
// transformclosure already did all preparation work.
// Prepend captured variables to argument list.
- n.List = concat(n.Left.Func.Enter, n.List)
+ n.List = concat(n.Left.Func.Enter.NodeList(), n.List)
- n.Left.Func.Enter = nil
+ n.Left.Func.Enter.Set(nil)
// Replace OCLOSURE with ONAME/PFUNC.
n.Left = n.Left.Func.Closure.Func.Nname
r := convas(Nod(OAS, n.Left, n.Right), init)
r.Dodata = n.Dodata
n = r
- n = applywritebarrier(n, init)
+ n = applywritebarrier(n)
}
case OAS2:
ll := ascompatee(OAS, n.List, n.Rlist, init)
ll = reorder3(ll)
for lr := ll; lr != nil; lr = lr.Next {
- lr.N = applywritebarrier(lr.N, init)
+ lr.N = applywritebarrier(lr.N)
}
n = liststmt(ll)
ll := ascompatet(n.Op, n.List, &r.Type, 0, init)
for lr := ll; lr != nil; lr = lr.Next {
- lr.N = applywritebarrier(lr.N, init)
+ lr.N = applywritebarrier(lr.N)
}
n = liststmt(concat(list1(r), ll))
// TODO(rsc): Perhaps componentgen should run before this.
-func applywritebarrier(n *Node, init **NodeList) *Node {
+func applywritebarrier(n *Node) *Node {
if n.Left != nil && n.Right != nil && needwritebarrier(n.Left, n.Right) {
if Debug_wb > 1 {
Warnl(int(n.Lineno), "marking %v for barrier", Nconv(n.Left, 0))
// walk through argin parameters.
// generate and return code to allocate
// copies of escaped parameters to the heap.
-func paramstoheap(argin **Type, out int) *NodeList {
+func paramstoheap(argin **Type, out int) []*Node {
var savet Iter
var v *Node
var as *Node
- var nn *NodeList
+ var nn []*Node
for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) {
v = t.Nname
if v != nil && v.Sym != nil && v.Sym.Name[0] == '~' && v.Sym.Name[1] == 'r' { // unnamed result
// Defer might stop a panic and show the
// return values as they exist at the time of panic.
// Make sure to zero them on entry to the function.
- nn = list(nn, Nod(OAS, nodarg(t, -1), nil))
+ nn = append(nn, Nod(OAS, nodarg(t, -1), nil))
}
if v == nil || v.Class&PHEAP == 0 {
if prealloc[v] == nil {
prealloc[v] = callnew(v.Type)
}
- nn = list(nn, Nod(OAS, v.Name.Heapaddr, prealloc[v]))
+ nn = append(nn, Nod(OAS, v.Name.Heapaddr, prealloc[v]))
if v.Class&^PHEAP != PPARAMOUT {
as = Nod(OAS, v, v.Name.Param.Stackparam)
v.Name.Param.Stackparam.Typecheck = 1
typecheck(&as, Etop)
- as = applywritebarrier(as, &nn)
- nn = list(nn, as)
+ as = applywritebarrier(as)
+ nn = append(nn, as)
}
}
}
// walk through argout parameters copying back to stack
-func returnsfromheap(argin **Type) *NodeList {
+func returnsfromheap(argin **Type) []*Node {
var savet Iter
var v *Node
- var nn *NodeList
+ var nn []*Node
for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) {
v = t.Nname
if v == nil || v.Class != PHEAP|PPARAMOUT {
continue
}
- nn = list(nn, Nod(OAS, v.Name.Param.Stackparam, v))
+ nn = append(nn, Nod(OAS, v.Name.Param.Stackparam, v))
}
return nn
lno := lineno
lineno = Curfn.Lineno
nn := paramstoheap(getthis(Curfn.Type), 0)
- nn = concat(nn, paramstoheap(getinarg(Curfn.Type), 0))
- nn = concat(nn, paramstoheap(Getoutarg(Curfn.Type), 1))
- Curfn.Func.Enter = concat(Curfn.Func.Enter, nn)
+ nn = append(nn, paramstoheap(getinarg(Curfn.Type), 0)...)
+ nn = append(nn, paramstoheap(Getoutarg(Curfn.Type), 1)...)
+ Curfn.Func.Enter.Append(nn...)
lineno = Curfn.Func.Endlineno
- Curfn.Func.Exit = returnsfromheap(Getoutarg(Curfn.Type))
+ Curfn.Func.Exit.Append(returnsfromheap(Getoutarg(Curfn.Type))...)
lineno = lno
}
)
func defframe(ptxt *obj.Prog) {
- var n *gc.Node
-
// fill in argument size, stack size
ptxt.To.Type = obj.TYPE_TEXTSIZE
lo := hi
// iterate through declarations - they are sorted in decreasing xoffset order.
- for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
- n = l.N
+ for _, n := range gc.Curfn.Func.Dcl {
if !n.Name.Needzero {
continue
}
)
func defframe(ptxt *obj.Prog) {
- var n *gc.Node
-
// fill in argument size, stack size
ptxt.To.Type = obj.TYPE_TEXTSIZE
lo := hi
// iterate through declarations - they are sorted in decreasing xoffset order.
- for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
- n = l.N
+ for _, n := range gc.Curfn.Func.Dcl {
if !n.Name.Needzero {
continue
}
// The hardware will generate undefined result.
// Also need to explicitly trap on division on zero,
// the hardware will silently generate undefined result.
- // DIVW will leave unpredicable result in higher 32-bit,
+ // DIVW will leave unpredictable result in higher 32-bit,
// so always use DIVD/DIVDU.
t := nl.Type
)
func defframe(ptxt *obj.Prog) {
- var n *gc.Node
-
// fill in argument size, stack size
ptxt.To.Type = obj.TYPE_TEXTSIZE
hi := int64(0)
lo := hi
ax := uint32(0)
- for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
- n = l.N
+ for _, n := range gc.Curfn.Func.Dcl {
if !n.Name.Needzero {
continue
}
}
if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST {
- // move or artihmetic into partial register.
+ // move or arithmetic into partial register.
// from another register or constant can be movl.
// we don't switch to 32-bit arithmetic if it can
// change how the carry bit is set (and the carry bit is needed).
import (
"bytes"
+ "encoding/json"
"flag"
"fmt"
"os"
{"runtime/internal/sys", []string{
"zversion.go",
}},
+ {"go/build", []string{
+ "zcgo.go",
+ }},
}
// depsuffix records the allowed suffixes for source files.
}{
{"zdefaultcc.go", mkzdefaultcc},
{"zversion.go", mkzversion},
+ {"zcgo.go", mkzcgo},
// not generated anymore, but delete the file if we see it
{"enam.c", nil},
"clean deletes all built files\n" +
"env [-p] print environment (-p: include $PATH)\n" +
"install [dir] install individual directory\n" +
+ "list [-json] list all supported platforms\n" +
"test [-h] run Go test(s)\n" +
"version print Go version\n" +
"\n" +
}
}
-// Copied from go/build/build.go.
// Cannot use go/build directly because cmd/dist for a new release
// builds against an old release's go/build, which may be out of sync.
+// To reduce duplication, we generate the list for go/build from this.
+//
+// We list all supported platforms in this list, so that this is the
+// single point of truth for supported platforms. This list is used
+// by 'go tool dist list'.
var cgoEnabled = map[string]bool{
"darwin/386": true,
"darwin/amd64": true,
"dragonfly/amd64": true,
"freebsd/386": true,
"freebsd/amd64": true,
+ "freebsd/arm": false,
"linux/386": true,
"linux/amd64": true,
"linux/arm": true,
"linux/arm64": true,
+ "linux/ppc64": false,
"linux/ppc64le": true,
+ "linux/mips64": false,
+ "linux/mips64le": false,
"android/386": true,
"android/amd64": true,
"android/arm": true,
+ "android/arm64": true,
+ "nacl/386": false,
+ "nacl/amd64p32": false,
+ "nacl/arm": false,
"netbsd/386": true,
"netbsd/amd64": true,
"netbsd/arm": true,
"openbsd/386": true,
"openbsd/amd64": true,
+ "openbsd/arm": false,
+ "plan9/386": false,
+ "plan9/amd64": false,
+ "plan9/arm": false,
"solaris/amd64": true,
"windows/386": true,
"windows/amd64": true,
fatal("current directory %s is not under %s", pwd, real_src)
}
pwd = pwd[len(real_src):]
- // guard againt xrealwd return the directory without the trailing /
+ // guard against xrealwd returning the directory without the trailing /
pwd = strings.TrimPrefix(pwd, "/")
return pwd
xflagparse(0)
xprintf("%s\n", findgoversion())
}
+
+// cmdlist lists all supported platforms.
+func cmdlist() {
+ jsonFlag := flag.Bool("json", false, "produce JSON output")
+ xflagparse(0)
+
+ var plats []string
+ for p := range cgoEnabled {
+ plats = append(plats, p)
+ }
+ sort.Strings(plats)
+
+ if !*jsonFlag {
+ for _, p := range plats {
+ xprintf("%s\n", p)
+ }
+ return
+ }
+
+ type jsonResult struct {
+ GOOS string
+ GOARCH string
+ CgoSupported bool
+ }
+ var results []jsonResult
+ for _, p := range plats {
+ fields := strings.Split(p, "/")
+ results = append(results, jsonResult{
+ GOOS: fields[0],
+ GOARCH: fields[1],
+ CgoSupported: cgoEnabled[p]})
+ }
+ out, err := json.MarshalIndent(results, "", "\t")
+ if err != nil {
+ fatal("json marshal error: %v", err)
+ }
+ if _, err := os.Stdout.Write(out); err != nil {
+ fatal("write failed: %v", err)
+ }
+}
package main
-import "fmt"
+import (
+ "bytes"
+ "fmt"
+)
/*
* Helpers for building cmd/go and cmd/cgo.
file = file[:i] + "c" + file[i:]
writefile(out, file, writeSkipSame)
}
+
+// mkzcgo writes zcgo.go for go/build package:
+//
+// package build
+// var cgoEnabled = map[string]bool{}
+//
+// It is invoked to write go/build/zcgo.go.
+func mkzcgo(dir, file string) {
+ var buf bytes.Buffer
+
+ fmt.Fprintf(&buf,
+ "// auto generated by go tool dist\n"+
+ "\n"+
+ "package build\n"+
+ "\n"+
+ "var cgoEnabled = map[string]bool{\n")
+ for plat, hasCgo := range cgoEnabled {
+ if hasCgo {
+ fmt.Fprintf(&buf, "\t%q: true,\n", plat)
+ }
+ }
+ fmt.Fprintf(&buf, "}")
+
+ writefile(buf.String(), file, writeSkipSame)
+}
{"clean", cmdclean},
{"env", cmdenv},
{"install", cmdinstall},
+ {"list", cmdlist},
{"test", cmdtest},
{"version", cmdversion},
}
return nil
},
})
+ fortran := os.Getenv("FC")
+ if fortran == "" {
+ fortran, _ = exec.LookPath("gfortran")
+ }
+ if fortran != "" {
+ t.tests = append(t.tests, distTest{
+ name: "cgo_fortran",
+ heading: "../misc/cgo/fortran",
+ fn: func(dt *distTest) error {
+ t.addCmd(dt, "misc/cgo/fortran", "./test.bash", fortran)
+ return nil
+ },
+ })
+ }
}
if t.cgoEnabled && t.goos != "android" && !t.iOS() {
// TODO(crawshaw): reenable on android and iOS
return fmt.Errorf("can't build package %s because it contains Objective-C files (%s) but it's not using cgo nor SWIG",
a.p.ImportPath, strings.Join(a.p.MFiles, ","))
}
+ // Same as above for Fortran files
+ if len(a.p.FFiles) > 0 && !a.p.usesCgo() && !a.p.usesSwig() {
+ return fmt.Errorf("can't build package %s because it contains Fortran files (%s) but it's not using cgo nor SWIG",
+ a.p.ImportPath, strings.Join(a.p.FFiles, ","))
+ }
defer func() {
if err != nil && err != errPrintedOutput {
err = fmt.Errorf("go build %s: %v", a.p.ImportPath, err)
if a.cgo != nil && a.cgo.target != "" {
cgoExe = a.cgo.target
}
- outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, cxxfiles, a.p.MFiles)
+ outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, cxxfiles, a.p.MFiles, a.p.FFiles)
if err != nil {
return err
}
// so that it can give good error messages about forward declarations.
// Exceptions: a few standard packages have forward declarations for
// pieces supplied behind-the-scenes by package runtime.
- extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
+ extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.FFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
if p.Standard {
switch p.ImportPath {
case "bytes", "net", "os", "runtime/pprof", "sync", "time":
usesCgo := false
cxx := len(root.p.CXXFiles) > 0 || len(root.p.SwigCXXFiles) > 0
objc := len(root.p.MFiles) > 0
+ fortran := len(root.p.FFiles) > 0
actionsSeen := make(map[*action]bool)
// Make a pre-order depth-first traversal of the action graph, taking note of
if len(a.p.MFiles) > 0 {
objc = true
}
+ if len(a.p.FFiles) > 0 {
+ fortran = true
+ }
}
ldflags = append(ldflags, "-Wl,--whole-archive")
if objc {
ldflags = append(ldflags, "-lobjc")
}
+ if fortran {
+ fc := os.Getenv("FC")
+ if fc == "" {
+ fc = "gfortran"
+ }
+ // support gfortran out of the box and let others pass the correct link options
+ // via CGO_LDFLAGS
+ if strings.Contains(fc, "gfortran") {
+ ldflags = append(ldflags, "-lgfortran")
+ }
+ }
}
if err := b.run(".", root.p.ImportPath, nil, tools.linker(), "-o", out, ofiles, ldflags, buildGccgoflags); err != nil {
return b.ccompile(p, out, flags, cxxfile, b.gxxCmd(p.Dir))
}
+// gfortran runs the gfortran Fortran compiler to create an object from a single Fortran file.
+func (b *builder) gfortran(p *Package, out string, flags []string, ffile string) error {
+ return b.ccompile(p, out, flags, ffile, b.gfortranCmd(p.Dir))
+}
+
// ccompile runs the given C or C++ compiler and creates an object from a single source file.
func (b *builder) ccompile(p *Package, out string, flags []string, file string, compiler []string) error {
file = mkAbs(p.Dir, file)
return b.ccompilerCmd("CXX", defaultCXX, objdir)
}
+// gfortranCmd returns a gfortran command line prefix.
+func (b *builder) gfortranCmd(objdir string) []string {
+ return b.ccompilerCmd("FC", "gfortran", objdir)
+}
+
// ccompilerCmd returns a command line prefix for the given environment
// variable and using the default command when the variable is empty.
func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string {
return strings.Fields(v)
}
-// Return the flags to use when invoking the C or C++ compilers, or cgo.
-func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, ldflags []string) {
+// Return the flags to use when invoking the C, C++ or Fortran compilers, or cgo.
+func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, fflags, ldflags []string) {
var defaults string
if def {
defaults = "-g -O2"
cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS)
cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS)
cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS)
+ fflags = stringList(envList("CGO_FFLAGS", defaults), p.CgoFFLAGS)
ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS)
return
}
var cgoRe = regexp.MustCompile(`[/\\:]`)
-func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) {
- cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := b.cflags(p, true)
- _, cgoexeCFLAGS, _, _ := b.cflags(p, false)
+func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles, ffiles []string) (outGo, outObj []string, err error) {
+ cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS, cgoLDFLAGS := b.cflags(p, true)
+ _, cgoexeCFLAGS, _, _, _ := b.cflags(p, false)
cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...)
cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...)
// If we are compiling Objective-C code, then we need to link against libobjc
cgoLDFLAGS = append(cgoLDFLAGS, "-lobjc")
}
+ // Likewise for Fortran, except there are many Fortran compilers.
+ // Support gfortran out of the box and let others pass the correct link options
+ // via CGO_LDFLAGS
+ if len(ffiles) > 0 {
+ fc := os.Getenv("FC")
+ if fc == "" {
+ fc = "gfortran"
+ }
+ if strings.Contains(fc, "gfortran") {
+ cgoLDFLAGS = append(cgoLDFLAGS, "-lgfortran")
+ }
+ }
+
if buildMSan && p.ImportPath != "runtime/cgo" {
cgoCFLAGS = append([]string{"-fsanitize=memory"}, cgoCFLAGS...)
cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...)
outObj = append(outObj, ofile)
}
+ fflags := stringList(cgoCPPFLAGS, cgoFFLAGS)
+ for _, file := range ffiles {
+ // Append .o to the file, just in case the pkg has file.c and file.f
+ ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o"
+ if err := b.gfortran(p, ofile, fflags, file); err != nil {
+ return nil, nil, err
+ }
+ linkobj = append(linkobj, ofile)
+ outObj = append(outObj, ofile)
+ }
+
linkobj = append(linkobj, p.SysoFiles...)
dynobj := obj + "_cgo_.o"
pie := (goarch == "arm" && goos == "linux") || goos == "android"
// Run SWIG on one SWIG input file.
func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outC string, err error) {
- cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true)
+ cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _, _ := b.cflags(p, true)
var cflags []string
if cxx {
cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCXXFLAGS)
delete(packageCache, name)
}
+ // In order to rebuild packages information completely,
+ // we need to clear commands cache. Command packages are
+ // referring to evicted packages from the package cache.
+ // This leads to duplicated loads of the standard packages.
+ for name := range cmdCache {
+ delete(cmdCache, name)
+ }
+
args = importPaths(args)
packagesForBuild(args)
// cmd/go: go test -a foo does not rebuild regexp.
func TestIssue6844(t *testing.T) {
if testing.Short() {
- t.Skip("don't rebuild the standard libary in short mode")
+ t.Skip("don't rebuild the standard library in short mode")
}
tg := testgo(t)
if !canCgo {
t.Skip("skipping because cgo not enabled")
}
+ if runtime.GOOS == "solaris" {
+ // See https://golang.org/issue/13247
+ t.Skip("skipping because Solaris builds are known to be inconsistent; see #13247")
+ }
tg := testgo(t)
defer tg.cleanup()
t.Error("building cgotest twice did not produce the same output")
}
}
+
+// Issue 14444: go get -u .../ duplicate loads errors
+func TestGoGetUpdateAllDoesNotTryToLoadDuplicates(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+
+ tg := testgo(t)
+ defer tg.cleanup()
+ tg.makeTempdir()
+ tg.setenv("GOPATH", tg.path("."))
+ tg.run("get", "-u", ".../")
+ tg.grepStderrNot("duplicate loads of", "did not remove old packages from cache")
+}
CXXFiles []string // .cc, .cxx and .cpp source files
MFiles []string // .m source files
HFiles []string // .h, .hh, .hpp and .hxx source files
+ FFiles []string // .f, .F, .for and .f90 Fortran source files
SFiles []string // .s source files
SwigFiles []string // .swig files
SwigCXXFiles []string // .swigcxx files
CgoCFLAGS []string // cgo: flags for C compiler
CgoCPPFLAGS []string // cgo: flags for C preprocessor
CgoCXXFLAGS []string // cgo: flags for C++ compiler
+ CgoFFLAGS []string // cgo: flags for Fortran compiler
CgoLDFLAGS []string // cgo: flags for linker
CgoPkgConfig []string // cgo: pkg-config names
CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files
MFiles []string `json:",omitempty"` // .m source files
HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files
+ FFiles []string `json:",omitempty"` // .f, .F, .for and .f90 Fortran source files
SFiles []string `json:",omitempty"` // .s source files
SwigFiles []string `json:",omitempty"` // .swig files
SwigCXXFiles []string `json:",omitempty"` // .swigcxx files
CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler
CgoCPPFLAGS []string `json:",omitempty"` // cgo: flags for C preprocessor
CgoCXXFLAGS []string `json:",omitempty"` // cgo: flags for C++ compiler
+ CgoFFLAGS []string `json:",omitempty"` // cgo: flags for Fortran compiler
CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker
CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names
p.CXXFiles = pp.CXXFiles
p.MFiles = pp.MFiles
p.HFiles = pp.HFiles
+ p.FFiles = pp.FFiles
p.SFiles = pp.SFiles
p.SwigFiles = pp.SwigFiles
p.SwigCXXFiles = pp.SwigCXXFiles
p.CXXFiles,
p.MFiles,
p.HFiles,
+ p.FFiles,
p.SFiles,
p.SysoFiles,
p.SwigFiles,
// an explicit data comparison. Specifically, we build a list of the
// inputs to the build, compute its SHA1 hash, and record that as the
// ``build ID'' in the generated object. At the next build, we can
-// recompute the buid ID and compare it to the one in the generated
+// recompute the build ID and compare it to the one in the generated
// object. If they differ, the list of inputs has changed, so the object
// is out of date and must be rebuilt.
//
// to test for write access, and then skip GOPATH roots we don't have write
// access to. But hopefully we can just use the mtimes always.
- srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles, p.SwigFiles, p.SwigCXXFiles)
+ srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.CgoFiles, p.SysoFiles, p.SwigFiles, p.SwigCXXFiles)
for _, src := range srcs {
if olderThan(filepath.Join(p.Dir, src)) {
return true
return nil
}
-// isTestMain tells whether fn is a TestMain(m *testing.M) function.
-func isTestMain(fn *ast.FuncDecl) bool {
- if fn.Name.String() != "TestMain" ||
- fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
- fn.Type.Params == nil ||
+// isTestFunc tells whether fn has the type of a testing function. arg
+// specifies the parameter type we look for: B, M or T.
+func isTestFunc(fn *ast.FuncDecl, arg string) bool {
+ if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
+ fn.Type.Params.List == nil ||
len(fn.Type.Params.List) != 1 ||
len(fn.Type.Params.List[0].Names) > 1 {
return false
// We can't easily check that the type is *testing.M
// because we don't know how testing has been imported,
// but at least check that it's *M or *something.M.
- if name, ok := ptr.X.(*ast.Ident); ok && name.Name == "M" {
+ // Same applies for B and T.
+ if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg {
return true
}
- if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == "M" {
+ if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg {
return true
}
return false
}
name := n.Name.String()
switch {
- case isTestMain(n):
+ case name == "TestMain" && isTestFunc(n, "M"):
if t.TestMain != nil {
return errors.New("multiple definitions of TestMain")
}
t.TestMain = &testFunc{pkg, name, ""}
*doImport, *seen = true, true
case isTest(name, "Test"):
+ err := checkTestFunc(n, "T")
+ if err != nil {
+ return err
+ }
t.Tests = append(t.Tests, testFunc{pkg, name, ""})
*doImport, *seen = true, true
case isTest(name, "Benchmark"):
+ err := checkTestFunc(n, "B")
+ if err != nil {
+ return err
+ }
t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, ""})
*doImport, *seen = true, true
}
return nil
}
+func checkTestFunc(fn *ast.FuncDecl, arg string) error {
+ if !isTestFunc(fn, arg) {
+ name := fn.Name.String()
+ pos := testFileSet.Position(fn.Pos())
+ return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg)
+ }
+ return nil
+}
+
type byOrder []*doc.Example
func (x byOrder) Len() int { return len(x) }
return errCorruptArchive
}
switch name {
- case "__.SYMDEF", "__.GOSYMDEF", "__.PKGDEF":
+ case "__.PKGDEF":
r.skip(size)
default:
oldLimit := r.limit
}
}
- // Replace TLS register fetches on older ARM procesors.
+ // Replace TLS register fetches on older ARM processors.
switch p.As {
// Treat MRC 15, 0, <reg>, C13, C0, 3 specially.
case AMRC:
R_PLT1
R_PLT2
R_USEFIELD
+ // R_USETYPE resolves to an *rtype, but no relocation is created. The
+ // linker uses this as a signal that the pointed-to type information
+ // should be linked into the final binary, even if there are no other
+ // direct references. (This is used for types reachable by reflection.)
+ R_USETYPE
R_POWER_TOC
R_GOTPCREL
// R_JMPMIPS (only used on mips64) resolves to non-PC-relative target address
Debugpcln int32
Flag_shared int32
Flag_dynlink bool
+ Flag_optimize bool
Bso *Biobuf
Pathname string
Windows int32
Data *LSym
Etext *LSym
Edata *LSym
+
+ // Cache of Progs
+ allocIdx int
+ progs [10000]Prog
}
func (ctxt *Link) Diag(format string, args ...interface{}) {
}
/*
- * test to see if 2 instrictions can be
+ * test to see if two instructions can be
* interchanged without changing semantics
*/
func depend(ctxt *obj.Link, sa, sb *Sch) bool {
}
func Flushplist(ctxt *Link) {
+ flushplist(ctxt, ctxt.Debugasm == 0)
+}
+func FlushplistNoFree(ctxt *Link) {
+ flushplist(ctxt, false)
+}
+func flushplist(ctxt *Link, freeProgs bool) {
var flag int
var s *LSym
var p *Prog
for s := text; s != nil; s = s.Next {
mkfwd(s)
linkpatch(ctxt, s)
- ctxt.Arch.Follow(ctxt, s)
+ if ctxt.Flag_optimize {
+ ctxt.Arch.Follow(ctxt, s)
+ }
ctxt.Arch.Preprocess(ctxt, s)
ctxt.Arch.Assemble(ctxt, s)
fieldtrack(ctxt, s)
linkpcln(ctxt, s)
+ if freeProgs {
+ s.Text = nil
+ s.Etext = nil
+ }
}
// Add to running list in ctxt.
- if ctxt.Etext == nil {
- ctxt.Text = text
- } else {
- ctxt.Etext.Next = text
+ if text != nil {
+ if ctxt.Text == nil {
+ ctxt.Text = text
+ } else {
+ ctxt.Etext.Next = text
+ }
+ ctxt.Etext = etext
}
- ctxt.Etext = etext
ctxt.Plist = nil
+ ctxt.Plast = nil
+ ctxt.Curp = nil
+ if freeProgs {
+ ctxt.freeProgs()
+ }
}
func Writeobjfile(ctxt *Link, b *Biobuf) {
p.Pcond = q
}
- for p := sym.Text; p != nil; p = p.Link {
- if p.Pcond != nil {
- p.Pcond = brloop(ctxt, p.Pcond)
+ if ctxt.Flag_optimize {
+ for p := sym.Text; p != nil; p = p.Link {
if p.Pcond != nil {
- if p.To.Type == TYPE_BRANCH {
- p.To.Offset = p.Pcond.Pc
+ p.Pcond = brloop(ctxt, p.Pcond)
+ if p.Pcond != nil {
+ if p.To.Type == TYPE_BRANCH {
+ p.To.Offset = p.Pcond.Pc
+ }
}
}
}
ctxt.Goarm = Getgoarm()
}
+ ctxt.Flag_optimize = true
return ctxt
}
}
func (p *Prog) String() string {
+ if p == nil {
+ return "<nil Prog>"
+ }
+
if p.Ctxt == nil {
return "<Prog without ctxt>"
}
}
func (ctxt *Link) NewProg() *Prog {
- p := new(Prog) // should be the only call to this; all others should use ctxt.NewProg
+ var p *Prog
+ if i := ctxt.allocIdx; i < len(ctxt.progs) {
+ p = &ctxt.progs[i]
+ ctxt.allocIdx = i + 1
+ } else {
+ p = new(Prog) // should be the only call to this; all others should use ctxt.NewProg
+ }
p.Ctxt = ctxt
return p
}
+func (ctxt *Link) freeProgs() {
+ s := ctxt.progs[:ctxt.allocIdx]
+ for i := range s {
+ s[i] = Prog{}
+ }
+ ctxt.allocIdx = 0
+}
func (ctxt *Link) Line(n int) string {
return ctxt.LineHist.LineString(n)
}
}
- // Rewrite 0 to $0 in 3rd argment to CMPPS etc.
+ // Rewrite 0 to $0 in 3rd argument to CMPPS etc.
// That's what the tables expect.
switch p.As {
case ACMPPD, ACMPPS, ACMPSD, ACMPSS:
}
// Preserve highest 8 bits of a, and do addition to lower 24-bit
-// of a and b; used to adjust ARM branch intruction's target
+// of a and b; used to adjust ARM branch instruction's target
func braddoff(a int32, b int32) int32 {
return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b))
}
}
case obj.Hdarwin: /* apple MACH */
- ld.Debug['w'] = 1 // disable DWARF generataion
+ ld.Debug['w'] = 1 // disable DWARF generation
ld.Machoinit()
ld.HEADR = ld.INITIAL_MACHO_HEADR
if ld.INITTEXT == -1 {
rs := r.Xsym
// ld64 has a bug handling MACHO_ARM64_RELOC_UNSIGNED with !extern relocation.
- // see cmd/internal/ld/data.go for details. The workarond is that don't use !extern
+ // see cmd/internal/ld/data.go for details. The workaround is that don't use !extern
// UNSIGNED relocation at all.
if rs.Type == obj.SHOSTOBJ || r.Type == obj.R_CALLARM64 || r.Type == obj.R_ADDRARM64 || r.Type == obj.R_ADDR {
if rs.Dynid < 0 {
DW_CHILDREN_yes = 0x01
)
-// Not from the spec, but logicaly belongs here
+// Not from the spec, but logically belongs here
const (
DW_CLS_ADDRESS = 0x01 + iota
DW_CLS_BLOCK
}
if needSym != 0 {
- // local names and hidden visiblity global names are unique
- // and should only reference by its index, not name, so we
- // don't bother to add them into hash table
+ // local names and hidden global names are unique
+ // and should only be referenced by their index, not name, so we
+ // don't bother to add them into the hash table
s = linknewsym(Ctxt, sym.name, Ctxt.Version)
s.Type |= obj.SHIDDEN
f *os.File
}
-const (
- symname = "__.GOSYMDEF"
- pkgname = "__.PKGDEF"
-)
+const pkgname = "__.PKGDEF"
var (
// Set if we see an object compiled by the host compiler that is not
return
}
- /* skip over optional __.GOSYMDEF and process __.PKGDEF */
+ /* process __.PKGDEF */
off := obj.Boffset(f)
var arhdr ArHdr
goto out
}
- if strings.HasPrefix(arhdr.name, symname) {
- off += l
- l = nextar(f, off, &arhdr)
- if l <= 0 {
- Diag("%s: short read on archive file symbol header", lib.File)
- goto out
- }
- }
-
if !strings.HasPrefix(arhdr.name, pkgname) {
Diag("%s: cannot find package header", lib.File)
goto out
* the individual symbols that are unused.
*
* loading every object will also make it possible to
- * load foreign objects not referenced by __.GOSYMDEF.
+ * load foreign objects not referenced by __.PKGDEF.
*/
for {
l = nextar(f, off, &arhdr)
if Linkmode == LinkInternal {
// For lldb, must say LC_VERSION_MIN_MACOSX or else
// it won't know that this Mach-O binary is from OS X
- // (could be iOS or WatchOS intead).
+ // (could be iOS or WatchOS instead).
// Go on iOS uses linkmode=external, and linkmode=external
// adds this itself. So we only need this code for linkmode=internal
// and we can assume OS X.
addressOrder
)
-// sort reoders the entries in a report based on the specified
+// sort reorders the entries in a report based on the specified
// ordering criteria. The result is sorted in decreasing order for
// numeric quantities, alphabetically for text, and increasing for
// addresses.
return fnodes, filename
}
-// adjustSourcePath adjusts the pathe for a source file by trimmming
+// adjustSourcePath adjusts the path for a source file by trimming
// known prefixes and searching for the file on all parents of the
// current working dir.
func adjustSourcePath(path string) (*os.File, string, error) {
Locks that are erroneously passed by value.
-Documentation examples
+Tests, benchmarks and documentation examples
-Flag: -example
+Flag: -tests
-Mistakes involving example tests, including examples with incorrect names or
-function signatures, or that document identifiers not in the package.
+Mistakes involving tests including functions with incorrect names or signatures
+and example tests that document identifiers not in the package.
Methods
+++ /dev/null
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "go/ast"
- "go/types"
- "strings"
- "unicode"
- "unicode/utf8"
-)
-
-func init() {
- register("example",
- "check for common mistaken usages of documentation examples",
- checkExample,
- funcDecl)
-}
-
-func isExampleSuffix(s string) bool {
- r, size := utf8.DecodeRuneInString(s)
- return size > 0 && unicode.IsLower(r)
-}
-
-// checkExample walks the documentation example functions checking for common
-// mistakes of misnamed functions, failure to map functions to existing
-// identifiers, etc.
-func checkExample(f *File, node ast.Node) {
- if !strings.HasSuffix(f.name, "_test.go") {
- return
- }
- var (
- pkg = f.pkg
- pkgName = pkg.typesPkg.Name()
- scopes = []*types.Scope{pkg.typesPkg.Scope()}
- lookup = func(name string) types.Object {
- for _, scope := range scopes {
- if o := scope.Lookup(name); o != nil {
- return o
- }
- }
- return nil
- }
- )
- if strings.HasSuffix(pkgName, "_test") {
- // Treat 'package foo_test' as an alias for 'package foo'.
- var (
- basePkg = strings.TrimSuffix(pkgName, "_test")
- pkg = f.pkg
- )
- for _, p := range pkg.typesPkg.Imports() {
- if p.Name() == basePkg {
- scopes = append(scopes, p.Scope())
- break
- }
- }
- }
- fn, ok := node.(*ast.FuncDecl)
- if !ok {
- // Ignore non-functions.
- return
- }
- var (
- fnName = fn.Name.Name
- report = func(format string, args ...interface{}) { f.Badf(node.Pos(), format, args...) }
- )
- if fn.Recv != nil || !strings.HasPrefix(fnName, "Example") {
- // Ignore methods and types not named "Example".
- return
- }
- if params := fn.Type.Params; len(params.List) != 0 {
- report("%s should be niladic", fnName)
- }
- if results := fn.Type.Results; results != nil && len(results.List) != 0 {
- report("%s should return nothing", fnName)
- }
- if fnName == "Example" {
- // Nothing more to do.
- return
- }
- if filesRun && !includesNonTest {
- // The coherence checks between a test and the package it tests
- // will report false positives if no non-test files have
- // been provided.
- return
- }
- var (
- exName = strings.TrimPrefix(fnName, "Example")
- elems = strings.SplitN(exName, "_", 3)
- ident = elems[0]
- obj = lookup(ident)
- )
- if ident != "" && obj == nil {
- // Check ExampleFoo and ExampleBadFoo.
- report("%s refers to unknown identifier: %s", fnName, ident)
- // Abort since obj is absent and no subsequent checks can be performed.
- return
- }
- if elemCnt := strings.Count(exName, "_"); elemCnt == 0 {
- // Nothing more to do.
- return
- }
- mmbr := elems[1]
- if ident == "" {
- // Check Example_suffix and Example_BadSuffix.
- if residual := strings.TrimPrefix(exName, "_"); !isExampleSuffix(residual) {
- report("%s has malformed example suffix: %s", fnName, residual)
- }
- return
- }
- if !isExampleSuffix(mmbr) {
- // Check ExampleFoo_Method and ExampleFoo_BadMethod.
- if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj == nil {
- report("%s refers to unknown field or method: %s.%s", fnName, ident, mmbr)
- }
- }
- if len(elems) == 3 && !isExampleSuffix(elems[2]) {
- // Check ExampleFoo_Method_suffix and ExampleFoo_Method_Badsuffix.
- report("%s has malformed example suffix: %s", fnName, elems[2])
- }
- return
-}
-// Test of examples.
-
package testdata
+import (
+ "testing"
+)
+
// Buf is a ...
type Buf []byte
func ExamplePuffer_Append() // ERROR "ExamplePuffer_Append refers to unknown identifier: Puffer"
func ExamplePuffer_suffix() // ERROR "ExamplePuffer_suffix refers to unknown identifier: Puffer"
+
+func nonTest() {} // OK because it doesn't start with "Test".
+
+func (Buf) TesthasReceiver() {} // OK because it has a receiver.
+
+func TestOKSuffix(*testing.T) {} // OK because first char after "Test" is Uppercase.
+
+func TestÃœnicodeWorks(*testing.T) {} // OK because the first char after "Test" is Uppercase.
+
+func TestbadSuffix(*testing.T) {} // ERROR "first letter after 'Test' must not be lowercase"
+
+func TestemptyImportBadSuffix(*T) {} // ERROR "first letter after 'Test' must not be lowercase"
+
+func Test(*testing.T) {} // OK "Test" on its own is considered a test.
+
+func Testify() {} // OK because it takes no parameters.
+
+func TesttooManyParams(*testing.T, string) {} // OK because it takes too many parameters.
+
+func TesttooManyNames(a, b *testing.T) {} // OK because it takes too many names.
+
+func TestnoTParam(string) {} // OK because it doesn't take a *testing.T
+
+func BenchmarkbadSuffix(*testing.B) {} // ERROR "first letter after 'Benchmark' must not be lowercase"
--- /dev/null
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+ "go/types"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+func init() {
+ register("tests",
+ "check for common mistaken usages of tests/documentation examples",
+ checkTestFunctions,
+ funcDecl)
+}
+
+func isExampleSuffix(s string) bool {
+ r, size := utf8.DecodeRuneInString(s)
+ return size > 0 && unicode.IsLower(r)
+}
+
+func isTestSuffix(name string) bool {
+ if len(name) == 0 {
+ // "Test" is ok.
+ return true
+ }
+ r, _ := utf8.DecodeRuneInString(name)
+ return !unicode.IsLower(r)
+}
+
+func isTestParam(typ ast.Expr, wantType string) bool {
+ ptr, ok := typ.(*ast.StarExpr)
+ if !ok {
+ // Not a pointer.
+ return false
+ }
+ // No easy way of making sure it's a *testing.T or *testing.B:
+ // ensure the name of the type matches.
+ if name, ok := ptr.X.(*ast.Ident); ok {
+ return name.Name == wantType
+ }
+ if sel, ok := ptr.X.(*ast.SelectorExpr); ok {
+ return sel.Sel.Name == wantType
+ }
+ return false
+}
+
+func lookup(name string, scopes []*types.Scope) types.Object {
+ for _, scope := range scopes {
+ if o := scope.Lookup(name); o != nil {
+ return o
+ }
+ }
+ return nil
+}
+
+func extendedScope(pkg *Package) []*types.Scope {
+ scopes := []*types.Scope{pkg.typesPkg.Scope()}
+
+ pkgName := pkg.typesPkg.Name()
+ if strings.HasPrefix(pkgName, "_test") {
+ basePkg := strings.TrimSuffix(pkgName, "_test")
+ for _, p := range pkg.typesPkg.Imports() {
+ if p.Name() == basePkg {
+ scopes = append(scopes, p.Scope())
+ break
+ }
+ }
+ }
+ return scopes
+}
+
+func checkExample(fn *ast.FuncDecl, pkg *Package, report reporter) {
+ fnName := fn.Name.Name
+ if params := fn.Type.Params; len(params.List) != 0 {
+ report("%s should be niladic", fnName)
+ }
+ if results := fn.Type.Results; results != nil && len(results.List) != 0 {
+ report("%s should return nothing", fnName)
+ }
+
+ if filesRun && !includesNonTest {
+ // The coherence checks between a test and the package it tests
+ // will report false positives if no non-test files have
+ // been provided.
+ return
+ }
+
+ if fnName == "Example" {
+ // Nothing more to do.
+ return
+ }
+
+ var (
+ exName = strings.TrimPrefix(fnName, "Example")
+ elems = strings.SplitN(exName, "_", 3)
+ ident = elems[0]
+ obj = lookup(ident, extendedScope(pkg))
+ )
+ if ident != "" && obj == nil {
+ // Check ExampleFoo and ExampleBadFoo.
+ report("%s refers to unknown identifier: %s", fnName, ident)
+ // Abort since obj is absent and no subsequent checks can be performed.
+ return
+ }
+ if len(elems) < 2 {
+ // Nothing more to do.
+ return
+ }
+
+ if ident == "" {
+ // Check Example_suffix and Example_BadSuffix.
+ if residual := strings.TrimPrefix(exName, "_"); !isExampleSuffix(residual) {
+ report("%s has malformed example suffix: %s", fnName, residual)
+ }
+ return
+ }
+
+ mmbr := elems[1]
+ if !isExampleSuffix(mmbr) {
+ // Check ExampleFoo_Method and ExampleFoo_BadMethod.
+ if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj == nil {
+ report("%s refers to unknown field or method: %s.%s", fnName, ident, mmbr)
+ }
+ }
+ if len(elems) == 3 && !isExampleSuffix(elems[2]) {
+ // Check ExampleFoo_Method_suffix and ExampleFoo_Method_Badsuffix.
+ report("%s has malformed example suffix: %s", fnName, elems[2])
+ }
+}
+
+func checkTest(fn *ast.FuncDecl, prefix string, report reporter) {
+ // Want functions with 0 results and 1 parameter.
+ if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
+ fn.Type.Params == nil ||
+ len(fn.Type.Params.List) != 1 ||
+ len(fn.Type.Params.List[0].Names) > 1 {
+ return
+ }
+
+ // The param must look like a *testing.T or *testing.B.
+ if !isTestParam(fn.Type.Params.List[0].Type, prefix[:1]) {
+ return
+ }
+
+ if !isTestSuffix(fn.Name.Name[len(prefix):]) {
+ report("%s has malformed name: first letter after '%s' must not be lowercase", fn.Name.Name, prefix)
+ }
+}
+
+type reporter func(format string, args ...interface{})
+
+// checkTestFunctions walks Test, Benchmark and Example functions checking
+// malformed names, wrong signatures and examples documenting inexistent
+// identifiers.
+func checkTestFunctions(f *File, node ast.Node) {
+ if !strings.HasSuffix(f.name, "_test.go") {
+ return
+ }
+
+ fn, ok := node.(*ast.FuncDecl)
+ if !ok || fn.Recv != nil {
+ // Ignore non-functions or functions with receivers.
+ return
+ }
+
+ report := func(format string, args ...interface{}) { f.Badf(node.Pos(), format, args...) }
+
+ switch {
+ case strings.HasPrefix(fn.Name.Name, "Example"):
+ checkExample(fn, f.pkg, report)
+ case strings.HasPrefix(fn.Name.Name, "Test"):
+ checkTest(fn, "Test", report)
+ case strings.HasPrefix(fn.Name.Name, "Benchmark"):
+ checkTest(fn, "Benchmark", report)
+ }
+}
}
if r0.Cmp(r1) == 0 {
- t.Errorf("%s: the nonce used for two diferent messages was the same", tag)
+ t.Errorf("%s: the nonce used for two different messages was the same", tag)
}
}
BEQ aligned // aligned detected - skip copy
// Copy the unaligned source data into the aligned temporary buffer
- // memove(to=4(R13), from=8(R13), n=12(R13)) - Corrupts all registers
+ // memmove(to=4(R13), from=8(R13), n=12(R13)) - Corrupts all registers
MOVW $buf, Rtable // to
MOVW $64, Rc0 // n
MOVM.IB [Rtable,Rdata,Rc0], (R13)
// Reader is a global, shared instance of a cryptographically
// strong pseudo-random generator.
//
-// On Unix-like systems, Reader reads from /dev/urandom.
// On Linux, Reader uses getrandom(2) if available, /dev/urandom otherwise.
+// On OpenBSD, Reader uses getentropy(2).
+// On other Unix-like systems, Reader reads from /dev/urandom.
// On Windows systems, Reader uses the CryptGenRandom API.
var Reader io.Reader
--- /dev/null
+// Copyright 2016 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 rand
+
+import (
+ "internal/syscall/unix"
+)
+
+func init() {
+ altGetRandom = getRandomOpenBSD
+}
+
+func getRandomOpenBSD(p []byte) (ok bool) {
+ // getentropy(2) returns a maximum of 256 bytes per call
+ for i := 0; i < len(p); i += 256 {
+ end := i + 256
+ if len(p) < end {
+ end = len(p)
+ }
+ err := unix.GetEntropy(p[i:end])
+ if err != nil {
+ return false
+ }
+ }
+ return true
+}
"io"
"net"
"strconv"
+ "strings"
)
type clientHandshakeState struct {
return errors.New("tls: NextProtos values too large")
}
- sni := c.config.ServerName
- // IP address literals are not permitted as SNI values. See
- // https://tools.ietf.org/html/rfc6066#section-3.
- if net.ParseIP(sni) != nil {
- sni = ""
- }
-
hello := &clientHelloMsg{
vers: c.config.maxVersion(),
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
ocspStapling: true,
scts: true,
- serverName: sni,
+ serverName: hostnameInSNI(c.config.ServerName),
supportedCurves: c.config.curvePreferences(),
supportedPoints: []uint8{pointFormatUncompressed},
nextProtoNeg: len(c.config.NextProtos) > 0,
return protos[0], true
}
+
+// hostnameInSNI converts name into an approriate hostname for SNI.
+// Literal IP addresses and absolute FQDNs are not permitted as SNI values.
+// See https://tools.ietf.org/html/rfc6066#section-3.
+func hostnameInSNI(name string) string {
+ host := name
+ if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
+ host = host[1 : len(host)-1]
+ }
+ if i := strings.LastIndex(host, "%"); i > 0 {
+ host = host[:i]
+ }
+ if net.ParseIP(host) != nil {
+ return ""
+ }
+ if len(name) > 0 && name[len(name)-1] == '.' {
+ name = name[:len(name)-1]
+ }
+ return name
+}
t.Fatalf("%s resumed: %v, expected: %v", test, hs.DidResume, didResume)
}
if didResume && (hs.PeerCertificates == nil || hs.VerifiedChains == nil) {
- t.Fatalf("expected non-nil certificates after resumption. Got peerCertificates: %#v, verifedCertificates: %#v", hs.PeerCertificates, hs.VerifiedChains)
+ t.Fatalf("expected non-nil certificates after resumption. Got peerCertificates: %#v, verifiedCertificates: %#v", hs.PeerCertificates, hs.VerifiedChains)
}
}
runClientTestTLS12(t, test)
}
-func TestNoIPAddressesInSNI(t *testing.T) {
- for _, ipLiteral := range []string{"1.2.3.4", "::1"} {
+var hostnameInSNITests = []struct {
+ in, out string
+}{
+ // Opaque string
+ {"", ""},
+ {"localhost", "localhost"},
+ {"foo, bar, baz and qux", "foo, bar, baz and qux"},
+
+ // DNS hostname
+ {"golang.org", "golang.org"},
+ {"golang.org.", "golang.org"},
+
+ // Literal IPv4 address
+ {"1.2.3.4", ""},
+
+ // Literal IPv6 address
+ {"::1", ""},
+ {"::1%lo0", ""}, // with zone identifier
+ {"[::1]", ""}, // as per RFC 5952 we allow the [] style as IPv6 literal
+ {"[::1%lo0]", ""},
+}
+
+func TestHostnameInSNI(t *testing.T) {
+ for _, tt := range hostnameInSNITests {
c, s := net.Pipe()
- go func() {
- client := Client(c, &Config{ServerName: ipLiteral})
- client.Handshake()
- }()
+ go func(host string) {
+ Client(c, &Config{ServerName: host, InsecureSkipVerify: true}).Handshake()
+ }(tt.in)
var header [5]byte
if _, err := io.ReadFull(s, header[:]); err != nil {
if _, err := io.ReadFull(s, record[:]); err != nil {
t.Fatal(err)
}
+
+ c.Close()
s.Close()
- if bytes.Index(record, []byte(ipLiteral)) != -1 {
- t.Errorf("IP literal %q found in ClientHello: %x", ipLiteral, record)
+ var m clientHelloMsg
+ if !m.unmarshal(record) {
+ t.Errorf("unmarshaling ClientHello for %q failed", tt.in)
+ continue
+ }
+ if tt.in != tt.out && m.serverName == tt.in {
+ t.Errorf("prohibited %q found in ClientHello: %x", tt.in, record)
+ }
+ if m.serverName != tt.out {
+ t.Errorf("expected %q not found in ClientHello: %x", tt.out, record)
}
}
}
package x509_test
import (
+ "crypto/dsa"
+ "crypto/ecdsa"
+ "crypto/rsa"
"crypto/x509"
"encoding/pem"
+ "fmt"
)
func ExampleCertificate_Verify() {
panic("failed to verify certificate: " + err.Error())
}
}
+
+func ExampleParsePKIXPublicKey() {
+ const pubPEM = `
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlRuRnThUjU8/prwYxbty
+WPT9pURI3lbsKMiB6Fn/VHOKE13p4D8xgOCADpdRagdT6n4etr9atzDKUSvpMtR3
+CP5noNc97WiNCggBjVWhs7szEe8ugyqF23XwpHQ6uV1LKH50m92MbOWfCtjU9p/x
+qhNpQQ1AZhqNy5Gevap5k8XzRmjSldNAFZMY7Yv3Gi+nyCwGwpVtBUwhuLzgNFK/
+yDtw2WcWmUU7NuC8Q6MWvPebxVtCfVp/iQU6q60yyt6aGOBkhAX0LpKAEhKidixY
+nP9PNVBvxgu3XZ4P36gZV6+ummKdBVnc3NqwBLu5+CcdRdusmHPHd5pHf4/38Z3/
+6qU2a/fPvWzceVTEgZ47QjFMTCTmCwNt29cvi7zZeQzjtwQgn4ipN9NibRH/Ax/q
+TbIzHfrJ1xa2RteWSdFjwtxi9C20HUkjXSeI4YlzQMH0fPX6KCE7aVePTOnB69I/
+a9/q96DiXZajwlpq3wFctrs1oXqBp5DVrCIj8hU2wNgB7LtQ1mCtsYz//heai0K9
+PhE4X6hiE0YmeAZjR0uHl8M/5aW9xCoJ72+12kKpWAa0SFRWLy6FejNYCYpkupVJ
+yecLk/4L1W0l6jQQZnWErXZYe0PNFcmwGXy1Rep83kfBRNKRy5tvocalLlwXLdUk
+AIU+2GKjyT3iMuzZxxFxPFMCAwEAAQ==
+-----END PUBLIC KEY-----`
+
+ block, _ := pem.Decode([]byte(pubPEM))
+ if block == nil {
+ panic("failed to parse PEM block containing the public key")
+ }
+
+ pub, err := x509.ParsePKIXPublicKey(block.Bytes)
+ if err != nil {
+ panic("failed to parse DER encoded public key: " + err.Error())
+ }
+
+ switch pub := pub.(type) {
+ case *rsa.PublicKey:
+ fmt.Println("pub is of type RSA:", pub)
+ case *dsa.PublicKey:
+ fmt.Println("pub is of type DSA:", pub)
+ case *ecdsa.PublicKey:
+ fmt.Println("pub is of type ECDSA:", pub)
+ default:
+ panic("unknown type of public key")
+ }
+}
}
if have < want {
- t.Errorf("insufficent overlap between cgo and non-cgo roots; want at least %d, have %d", want, have)
+ t.Errorf("insufficient overlap between cgo and non-cgo roots; want at least %d, have %d", want, have)
}
}
// ParsePKIXPublicKey parses a DER encoded public key. These values are
// typically found in PEM blocks with "BEGIN PUBLIC KEY".
+//
+// Supported key types include RSA, DSA, and ECDSA. Unknown key
+// types result in an error.
+//
+// On success, pub will be of type *rsa.PublicKey, *dsa.PublicKey,
+// or *ecdsa.PublicKey.
func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error) {
var pki publicKeyInfo
if rest, err := asn1.Unmarshal(derBytes, &pki); err != nil {
_, err := db.Query("SELECT|non_existent|name|")
if err == nil {
- t.Fatal("Quering non-existent table should fail")
+ t.Fatal("Querying non-existent table should fail")
}
}
Class Class
}
-// A Class is the DWARF 4 class of an attibute value.
+// A Class is the DWARF 4 class of an attribute value.
//
// In general, a given attribute's value may take on one of several
// possible classes defined by DWARF, each of which leads to a
if err := cmd.Run(); err != nil {
t.Fatal(err)
}
- cmd = exec.Command("go", "tool", "link", "-H", "linux", "-E", "main",
+ cmd = exec.Command("go", "tool", "link", "-H", "linux",
"-o", pclinetestBinary, pclinetestBinary+".o")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
_, err := StdEncoding.Decode(dbuf, []byte(tc.input))
if tc.offset == -1 {
if err != nil {
- t.Error("Decoder wrongly detected coruption in", tc.input)
+ t.Error("Decoder wrongly detected corruption in", tc.input)
}
continue
}
_, err := StdEncoding.Decode(dbuf, []byte(tc.input))
if tc.offset == -1 {
if err != nil {
- t.Error("Decoder wrongly detected coruption in", tc.input)
+ t.Error("Decoder wrongly detected corruption in", tc.input)
}
continue
}
// non-doubled quote may appear in a quoted field.
//
// If TrimLeadingSpace is true, leading white space in a field is ignored.
+// If the field delimiter is white space, TrimLeadingSpace will trim the
+// delimiter.
type Reader struct {
Comma rune // field delimiter (set to ',' by NewReader)
Comment rune // comment character for start of line
for i := 0; i < b.N; i++ {
var r codeResponse
if err := Unmarshal(codeJSON, &r); err != nil {
- b.Fatal("Unmmarshal:", err)
+ b.Fatal("Unmarshal:", err)
}
}
b.SetBytes(int64(len(codeJSON)))
var r codeResponse
for i := 0; i < b.N; i++ {
if err := Unmarshal(codeJSON, &r); err != nil {
- b.Fatal("Unmmarshal:", err)
+ b.Fatal("Unmarshal:", err)
}
}
}
sort.Strings(varKeys)
}
-// Get retrieves a named exported variable.
+// Get retrieves a named exported variable. It returns nil if the name has
+// not been registered.
func Get(name string) Var {
mutex.RLock()
defer mutex.RUnlock()
varKeys = nil
}
+func TestNil(t *testing.T) {
+ RemoveAll()
+ val := Get("missing")
+ if val != nil {
+ t.Errorf("got %v, want nil", val)
+ }
+}
+
func TestInt(t *testing.T) {
RemoveAll()
reqs := NewInt("requests")
// Make sure we can handle very large widths.
{"%0100f", -1.0, zeroFill("-", 99, "1.000000")},
+ // Use spaces instead of zero if padding to the right.
+ {"%0-5s", "abc", "abc "},
+ {"%-05.1f", 1.0, "1.0 "},
+
// Complex fmt used to leave the plus flag set for future entries in the array
// causing +2+0i and +3+0i instead of 2+0i and 3+0i.
{"%v", []complex64{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"},
}
}
+func BenchmarkSprintfPadding(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ Sprintf("%16f", 1.0)
+ }
+ })
+}
+
func BenchmarkSprintfEmpty(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
}
})
}
+func BenchmarkSprintfBoolean(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ Sprintf("%t", true)
+ }
+ })
+}
func BenchmarkManyArgs(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
unsigned = false
)
-var padZeroBytes = make([]byte, nByte)
-var padSpaceBytes = make([]byte, nByte)
-
-func init() {
- for i := 0; i < nByte; i++ {
- padZeroBytes[i] = '0'
- padSpaceBytes[i] = ' '
- }
-}
-
// flags placed in a separate struct for easy clearing.
type fmtFlags struct {
widPresent bool
f.clearflags()
}
-// computePadding computes left and right padding widths (only one will be non-zero).
-func (f *fmt) computePadding(width int) (padding []byte, leftWidth, rightWidth int) {
- left := !f.minus
- w := f.wid
- if w < 0 {
- left = false
- w = -w
- }
- w -= width
- if w > 0 {
- if left && f.zero {
- return padZeroBytes, w, 0
- }
- if left {
- return padSpaceBytes, w, 0
- } else {
- // can't be zero padding on the right
- return padSpaceBytes, 0, w
- }
- }
- return
-}
-
// writePadding generates n bytes of padding.
-func (f *fmt) writePadding(n int, padding []byte) {
- for n > 0 {
- m := n
- if m > nByte {
- m = nByte
- }
- f.buf.Write(padding[0:m])
- n -= m
+func (f *fmt) writePadding(n int) {
+ if n <= 0 { // No padding bytes needed.
+ return
}
+ buf := *f.buf
+ oldLen := len(buf)
+ newLen := oldLen + n
+ // Make enough room for padding.
+ if newLen > cap(buf) {
+ buf = make(buffer, cap(buf)*2+n)
+ copy(buf, *f.buf)
+ }
+ // Decide which byte the padding should be filled with.
+ padByte := byte(' ')
+ if f.zero {
+ padByte = byte('0')
+ }
+ // Fill padding with padByte.
+ padding := buf[oldLen:newLen]
+ for i := range padding {
+ padding[i] = padByte
+ }
+ *f.buf = buf[:newLen]
}
-// pad appends b to f.buf, padded on left (w > 0) or right (w < 0 or f.minus).
+// pad appends b to f.buf, padded on left (!f.minus) or right (f.minus).
func (f *fmt) pad(b []byte) {
if !f.widPresent || f.wid == 0 {
f.buf.Write(b)
return
}
- padding, left, right := f.computePadding(utf8.RuneCount(b))
- if left > 0 {
- f.writePadding(left, padding)
- }
- f.buf.Write(b)
- if right > 0 {
- f.writePadding(right, padding)
+ width := f.wid - utf8.RuneCount(b)
+ if !f.minus {
+ // left padding
+ f.writePadding(width)
+ f.buf.Write(b)
+ } else {
+ // right padding
+ f.buf.Write(b)
+ f.writePadding(width)
}
}
-// padString appends s to buf, padded on left (w > 0) or right (w < 0 or f.minus).
+// padString appends s to f.buf, padded on left (!f.minus) or right (f.minus).
func (f *fmt) padString(s string) {
if !f.widPresent || f.wid == 0 {
f.buf.WriteString(s)
return
}
- padding, left, right := f.computePadding(utf8.RuneCountInString(s))
- if left > 0 {
- f.writePadding(left, padding)
- }
- f.buf.WriteString(s)
- if right > 0 {
- f.writePadding(right, padding)
+ width := f.wid - utf8.RuneCountInString(s)
+ if !f.minus {
+ // left padding
+ f.writePadding(width)
+ f.buf.WriteString(s)
+ } else {
+ // right padding
+ f.buf.WriteString(s)
+ f.writePadding(width)
}
}
-var (
- trueBytes = []byte("true")
- falseBytes = []byte("false")
-)
-
// fmt_boolean formats a boolean.
func (f *fmt) fmt_boolean(v bool) {
if v {
- f.pad(trueBytes)
+ f.padString("true")
} else {
- f.pad(falseBytes)
+ f.padString("false")
}
}
f.space = oldSpace
f.plus = oldPlus
f.wid = oldWid
- f.buf.Write(irparenBytes)
+ f.buf.WriteString("i)")
}
"unicode/utf8"
)
-// Some constants in the form of bytes, to avoid string overhead.
-// Needlessly fastidious, I suppose.
-var (
- commaSpaceBytes = []byte(", ")
- nilAngleBytes = []byte("<nil>")
- nilParenBytes = []byte("(nil)")
- nilBytes = []byte("nil")
- mapBytes = []byte("map[")
- percentBangBytes = []byte("%!")
- missingBytes = []byte("(MISSING)")
- badIndexBytes = []byte("(BADINDEX)")
- panicBytes = []byte("(PANIC=")
- extraBytes = []byte("%!(EXTRA ")
- irparenBytes = []byte("i)")
- bytesBytes = []byte("[]byte{")
- badWidthBytes = []byte("%!(BADWIDTH)")
- badPrecBytes = []byte("%!(BADPREC)")
- noVerbBytes = []byte("%!(NOVERB)")
+// Strings for use with buffer.WriteString.
+// This is less overhead than using buffer.Write with byte arrays.
+const (
+ commaSpaceString = ", "
+ nilAngleString = "<nil>"
+ nilParenString = "(nil)"
+ nilString = "nil"
+ mapString = "map["
+ percentBangString = "%!"
+ missingString = "(MISSING)"
+ badIndexString = "(BADINDEX)"
+ panicString = "(PANIC="
+ extraString = "%!(EXTRA "
+ bytesString = "[]byte{"
+ badWidthString = "%!(BADWIDTH)"
+ badPrecString = "%!(BADPREC)"
+ noVerbString = "%!(NOVERB)"
+ invReflectString = "<invalid reflect.Value>"
)
// State represents the printer state passed to custom formatters.
// Use simple []byte instead of bytes.Buffer to avoid large dependency.
type buffer []byte
-func (b *buffer) Write(p []byte) (n int, err error) {
+func (b *buffer) Write(p []byte) {
*b = append(*b, p...)
- return len(p), nil
}
-func (b *buffer) WriteString(s string) (n int, err error) {
+func (b *buffer) WriteString(s string) {
*b = append(*b, s...)
- return len(s), nil
}
-func (b *buffer) WriteByte(c byte) error {
+func (b *buffer) WriteByte(c byte) {
*b = append(*b, c)
- return nil
}
-func (bp *buffer) WriteRune(r rune) error {
+func (bp *buffer) WriteRune(r rune) {
if r < utf8.RuneSelf {
*bp = append(*bp, byte(r))
- return nil
+ return
}
b := *bp
}
w := utf8.EncodeRune(b[n:n+utf8.UTFMax], r)
*bp = b[:n+w]
- return nil
}
type pp struct {
return false
}
-func (p *pp) add(c rune) {
- p.buf.WriteRune(c)
-}
-
// Implement Write so we can call Fprintf on a pp (through State), for
// recursive use in custom verbs.
func (p *pp) Write(b []byte) (ret int, err error) {
- return p.buf.Write(b)
+ p.buf.Write(b)
+ return len(b), nil
}
// These routines end in 'f' and take a format string.
func (p *pp) unknownType(v reflect.Value) {
if !v.IsValid() {
- p.buf.Write(nilAngleBytes)
+ p.buf.WriteString(nilAngleString)
return
}
p.buf.WriteByte('?')
func (p *pp) badVerb(verb rune) {
p.erroring = true
- p.add('%')
- p.add('!')
- p.add(verb)
- p.add('(')
+ p.buf.WriteString(percentBangString)
+ p.buf.WriteRune(verb)
+ p.buf.WriteByte('(')
switch {
case p.arg != nil:
p.buf.WriteString(reflect.TypeOf(p.arg).String())
- p.add('=')
+ p.buf.WriteByte('=')
p.printArg(p.arg, 'v', 0)
case p.value.IsValid():
p.buf.WriteString(p.value.Type().String())
- p.add('=')
+ p.buf.WriteByte('=')
p.printValue(p.value, 'v', 0)
default:
- p.buf.Write(nilAngleBytes)
+ p.buf.WriteString(nilAngleString)
}
- p.add(')')
+ p.buf.WriteByte(')')
p.erroring = false
}
p.buf.WriteString("[]byte(nil)")
} else {
p.buf.WriteString(typ.String())
- p.buf.Write(nilParenBytes)
+ p.buf.WriteString(nilParenString)
}
return
}
if typ == nil {
- p.buf.Write(bytesBytes)
+ p.buf.WriteString(bytesString)
} else {
p.buf.WriteString(typ.String())
p.buf.WriteByte('{')
for i, c := range v {
if i > 0 {
if p.fmt.sharpV {
- p.buf.Write(commaSpaceBytes)
+ p.buf.WriteString(commaSpaceString)
} else {
p.buf.WriteByte(' ')
}
}
if p.fmt.sharpV {
- p.add('(')
+ p.buf.WriteByte('(')
p.buf.WriteString(value.Type().String())
- p.add(')')
- p.add('(')
+ p.buf.WriteString(")(")
if u == 0 {
- p.buf.Write(nilBytes)
+ p.buf.WriteString(nilString)
} else {
p.fmt0x64(uint64(u), true)
}
- p.add(')')
+ p.buf.WriteByte(')')
} else if verb == 'v' && u == 0 {
- p.buf.Write(nilAngleBytes)
+ p.buf.WriteString(nilAngleString)
} else {
if use0x64 {
p.fmt0x64(uint64(u), !p.fmt.sharp)
// Stringer that fails to guard against nil or a nil pointer for a
// value receiver, and in either case, "<nil>" is a nice result.
if v := reflect.ValueOf(arg); v.Kind() == reflect.Ptr && v.IsNil() {
- p.buf.Write(nilAngleBytes)
+ p.buf.WriteString(nilAngleString)
return
}
// Otherwise print a concise panic message. Most of the time the panic
panic(err)
}
p.fmt.clearflags() // We are done, and for this output we want default behavior.
- p.buf.Write(percentBangBytes)
- p.add(verb)
- p.buf.Write(panicBytes)
+ p.buf.WriteString(percentBangString)
+ p.buf.WriteRune(verb)
+ p.buf.WriteString(panicString)
p.panicking = true
p.printArg(err, 'v', 0)
p.panicking = false
if arg == nil {
if verb == 'T' || verb == 'v' {
- p.fmt.pad(nilAngleBytes)
+ p.fmt.padString(nilAngleString)
} else {
p.badVerb(verb)
}
func (p *pp) printValue(value reflect.Value, verb rune, depth int) (wasString bool) {
if !value.IsValid() {
if verb == 'T' || verb == 'v' {
- p.buf.Write(nilAngleBytes)
+ p.buf.WriteString(nilAngleString)
} else {
p.badVerb(verb)
}
BigSwitch:
switch f := value; f.Kind() {
case reflect.Invalid:
- p.buf.WriteString("<invalid reflect.Value>")
+ p.buf.WriteString(invReflectString)
case reflect.Bool:
p.fmtBool(f.Bool(), verb)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if p.fmt.sharpV {
p.buf.WriteString(f.Type().String())
if f.IsNil() {
- p.buf.WriteString("(nil)")
+ p.buf.WriteString(nilParenString)
break
}
p.buf.WriteByte('{')
} else {
- p.buf.Write(mapBytes)
+ p.buf.WriteString(mapString)
}
keys := f.MapKeys()
for i, key := range keys {
if i > 0 {
if p.fmt.sharpV {
- p.buf.Write(commaSpaceBytes)
+ p.buf.WriteString(commaSpaceString)
} else {
p.buf.WriteByte(' ')
}
if p.fmt.sharpV {
p.buf.WriteString(value.Type().String())
}
- p.add('{')
+ p.buf.WriteByte('{')
v := f
t := v.Type()
for i := 0; i < v.NumField(); i++ {
if i > 0 {
if p.fmt.sharpV {
- p.buf.Write(commaSpaceBytes)
+ p.buf.WriteString(commaSpaceString)
} else {
p.buf.WriteByte(' ')
}
if !value.IsValid() {
if p.fmt.sharpV {
p.buf.WriteString(f.Type().String())
- p.buf.Write(nilParenBytes)
+ p.buf.WriteString(nilParenString)
} else {
- p.buf.Write(nilAngleBytes)
+ p.buf.WriteString(nilAngleString)
}
} else {
wasString = p.printValue(value, verb, depth+1)
for i := 0; i < f.Len(); i++ {
if i > 0 {
if p.fmt.sharpV {
- p.buf.Write(commaSpaceBytes)
+ p.buf.WriteString(commaSpaceString)
} else {
p.buf.WriteByte(' ')
}
p.fmt.wid, p.fmt.widPresent, argNum = intFromArg(a, argNum)
if !p.fmt.widPresent {
- p.buf.Write(badWidthBytes)
+ p.buf.WriteString(badWidthString)
}
// We have a negative width, so take its value and ensure
p.fmt.precPresent = false
}
if !p.fmt.precPresent {
- p.buf.Write(badPrecBytes)
+ p.buf.WriteString(badPrecString)
}
afterIndex = false
} else {
}
if i >= end {
- p.buf.Write(noVerbBytes)
+ p.buf.WriteString(noVerbString)
continue
}
c, w := utf8.DecodeRuneInString(format[i:])
continue
}
if !p.goodArgNum {
- p.buf.Write(percentBangBytes)
- p.add(c)
- p.buf.Write(badIndexBytes)
+ p.buf.WriteString(percentBangString)
+ p.buf.WriteRune(c)
+ p.buf.WriteString(badIndexString)
continue
} else if argNum >= len(a) { // out of operands
- p.buf.Write(percentBangBytes)
- p.add(c)
- p.buf.Write(missingBytes)
+ p.buf.WriteString(percentBangString)
+ p.buf.WriteRune(c)
+ p.buf.WriteString(missingString)
continue
}
arg := a[argNum]
p.fmt.plusV = true
}
}
+
+ // Use space padding instead of zero padding to the right.
+ if p.fmt.minus {
+ p.fmt.zero = false
+ }
+
p.printArg(arg, c, 0)
}
// out of order, in which case it's too expensive to detect if they've all
// been used and arguably OK if they're not.
if !p.reordered && argNum < len(a) {
- p.buf.Write(extraBytes)
+ p.buf.WriteString(extraString)
for ; argNum < len(a); argNum++ {
arg := a[argNum]
if arg != nil {
}
p.printArg(arg, 'v', 0)
if argNum+1 < len(a) {
- p.buf.Write(commaSpaceBytes)
+ p.buf.WriteString(commaSpaceString)
}
}
p.buf.WriteByte(')')
"unicode/utf8"
)
-// runeUnreader is the interface to something that can unread runes.
-// If the object provided to Scan does not satisfy this interface,
-// a local buffer will be used to back up the input, but its contents
-// will be lost when Scan returns.
-type runeUnreader interface {
- UnreadRune() error
-}
-
// ScanState represents the scanner state passed to custom scanners.
// Scanners may do rune-at-a-time scanning or ask the ScanState
// to discover the next space-delimited token.
// ss is the internal implementation of ScanState.
type ss struct {
- rr io.RuneReader // where to read input
- buf buffer // token accumulator
- peekRune rune // one-rune lookahead
- prevRune rune // last rune returned by ReadRune
- count int // runes consumed so far.
- atEOF bool // already read EOF
+ rs io.RuneScanner // where to read input
+ buf buffer // token accumulator
+ count int // runes consumed so far.
+ atEOF bool // already read EOF
ssave
}
}
func (s *ss) ReadRune() (r rune, size int, err error) {
- if s.peekRune >= 0 {
- s.count++
- r = s.peekRune
- size = utf8.RuneLen(r)
- s.prevRune = r
- s.peekRune = -1
- return
- }
- if s.atEOF || s.nlIsEnd && s.prevRune == '\n' || s.count >= s.argLimit {
+ if s.atEOF || s.count >= s.argLimit {
err = io.EOF
return
}
- r, size, err = s.rr.ReadRune()
+ r, size, err = s.rs.ReadRune()
if err == nil {
s.count++
- s.prevRune = r
+ if s.nlIsEnd && r == '\n' {
+ s.atEOF = true
+ }
} else if err == io.EOF {
s.atEOF = true
}
}
func (s *ss) UnreadRune() error {
- if u, ok := s.rr.(runeUnreader); ok {
- u.UnreadRune()
- } else {
- s.peekRune = s.prevRune
- }
- s.prevRune = -1
+ s.rs.UnreadRune()
+ s.atEOF = false
s.count--
return nil
}
}
// readRune is a structure to enable reading UTF-8 encoded code points
-// from an io.Reader. It is used if the Reader given to the scanner does
-// not already implement io.RuneReader.
+// from an io.Reader. It is used if the Reader given to the scanner does
+// not already implement io.RuneScanner.
type readRune struct {
- reader io.Reader
- buf [utf8.UTFMax]byte // used only inside ReadRune
- pending int // number of bytes in pendBuf; only >0 for bad UTF-8
- pendBuf [utf8.UTFMax]byte // bytes left over
+ reader io.Reader
+ buf [utf8.UTFMax]byte // used only inside ReadRune
+ pending int // number of bytes in pendBuf; only >0 for bad UTF-8
+ pendBuf [utf8.UTFMax]byte // bytes left over
+ peekRune rune // if >=0 next rune; when <0 is ^(previous Rune)
}
// readByte returns the next byte from the input, which may be
r.pending--
return
}
- n, err := io.ReadFull(r.reader, r.pendBuf[0:1])
- if n != 1 {
- return 0, err
+ _, err = r.reader.Read(r.pendBuf[:1])
+ if err != nil {
+ return
}
return r.pendBuf[0], err
}
-// unread saves the bytes for the next read.
-func (r *readRune) unread(buf []byte) {
- copy(r.pendBuf[r.pending:], buf)
- r.pending += len(buf)
-}
-
// ReadRune returns the next UTF-8 encoded code point from the
// io.Reader inside r.
func (r *readRune) ReadRune() (rr rune, size int, err error) {
+ if r.peekRune >= 0 {
+ rr = r.peekRune
+ r.peekRune = ^r.peekRune
+ size = utf8.RuneLen(rr)
+ return
+ }
r.buf[0], err = r.readByte()
if err != nil {
- return 0, 0, err
+ return
}
if r.buf[0] < utf8.RuneSelf { // fast check for common ASCII case
rr = rune(r.buf[0])
size = 1 // Known to be 1.
+ // Flip the bits of the rune so it's available to UnreadRune.
+ r.peekRune = ^rr
return
}
var n int
- for n = 1; !utf8.FullRune(r.buf[0:n]); n++ {
+ for n = 1; !utf8.FullRune(r.buf[:n]); n++ {
r.buf[n], err = r.readByte()
if err != nil {
if err == io.EOF {
return
}
}
- rr, size = utf8.DecodeRune(r.buf[0:n])
- if size < n { // an error
- r.unread(r.buf[size:n])
+ rr, size = utf8.DecodeRune(r.buf[:n])
+ if size < n { // an error, save the bytes for the next read
+ copy(r.pendBuf[r.pending:], r.buf[size:n])
+ r.pending += n - size
}
+ // Flip the bits of the rune so it's available to UnreadRune.
+ r.peekRune = ^rr
return
}
+func (r *readRune) UnreadRune() error {
+ if r.peekRune >= 0 {
+ return errors.New("fmt: scanning called UnreadRune with no rune available")
+ }
+ // Reverse bit flip of previously read rune to obtain valid >=0 state.
+ r.peekRune = ^r.peekRune
+ return nil
+}
+
var ssFree = sync.Pool{
New: func() interface{} { return new(ss) },
}
// newScanState allocates a new ss struct or grab a cached one.
func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) {
s = ssFree.Get().(*ss)
- if rr, ok := r.(io.RuneReader); ok {
- s.rr = rr
+ if rs, ok := r.(io.RuneScanner); ok {
+ s.rs = rs
} else {
- s.rr = &readRune{reader: r}
+ s.rs = &readRune{reader: r, peekRune: -1}
}
s.nlIsSpace = nlIsSpace
s.nlIsEnd = nlIsEnd
- s.prevRune = -1
- s.peekRune = -1
s.atEOF = false
s.limit = hugeWid
s.argLimit = hugeWid
return
}
s.buf = s.buf[:0]
- s.rr = nil
+ s.rs = nil
ssFree.Put(s)
}
return string(s.buf)
case '"':
// Double-quoted: Include the quotes and let strconv.Unquote do the backslash escapes.
- s.buf.WriteRune(quote)
+ s.buf.WriteByte('"')
for {
r := s.mustReadRune()
s.buf.WriteRune(r)
}
// doScanf does the real work when scanning with a format string.
-// At the moment, it handles only pointers to basic types.
+// At the moment, it handles only pointers to basic types.
func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err error) {
defer errorHandler(&err)
end := len(format) - 1
}
}
+func BenchmarkScanRecursiveIntReaderWrapper(b *testing.B) {
+ b.ResetTimer()
+ ints := makeInts(intCount)
+ var r RecursiveInt
+ for i := b.N - 1; i >= 0; i-- {
+ buf := newReader(string(ints))
+ b.StartTimer()
+ Fscan(buf, &r)
+ b.StopTimer()
+ }
+}
+
// Issue 9124.
// %x on bytes couldn't handle non-space bytes terminating the scan.
func TestHexBytes(t *testing.T) {
// if set, or else the compiled code's GOARCH, GOOS, and GOROOT.
var Default Context = defaultContext()
-// Also known to cmd/dist/build.go.
-var cgoEnabled = map[string]bool{
- "darwin/386": true,
- "darwin/amd64": true,
- "darwin/arm": true,
- "darwin/arm64": true,
- "dragonfly/amd64": true,
- "freebsd/386": true,
- "freebsd/amd64": true,
- "freebsd/arm": true,
- "linux/386": true,
- "linux/amd64": true,
- "linux/arm": true,
- "linux/arm64": true,
- "linux/ppc64le": true,
- "android/386": true,
- "android/amd64": true,
- "android/arm": true,
- "netbsd/386": true,
- "netbsd/amd64": true,
- "netbsd/arm": true,
- "openbsd/386": true,
- "openbsd/amd64": true,
- "solaris/amd64": true,
- "windows/386": true,
- "windows/amd64": true,
-}
-
func defaultContext() Context {
var c Context
CXXFiles []string // .cc, .cpp and .cxx source files
MFiles []string // .m (Objective-C) source files
HFiles []string // .h, .hh, .hpp and .hxx source files
+ FFiles []string // .f, .F, .for and .f90 Fortran source files
SFiles []string // .s source files
SwigFiles []string // .swig files
SwigCXXFiles []string // .swigcxx files
CgoCFLAGS []string // Cgo CFLAGS directives
CgoCPPFLAGS []string // Cgo CPPFLAGS directives
CgoCXXFLAGS []string // Cgo CXXFLAGS directives
+ CgoFFLAGS []string // Cgo FFLAGS directives
CgoLDFLAGS []string // Cgo LDFLAGS directives
CgoPkgConfig []string // Cgo pkg-config directives
case ".h", ".hh", ".hpp", ".hxx":
p.HFiles = append(p.HFiles, name)
continue
+ case ".f", ".F", ".for", ".f90":
+ p.FFiles = append(p.FFiles, name)
+ continue
case ".s":
p.SFiles = append(p.SFiles, name)
continue
}
switch ext {
- case ".go", ".c", ".cc", ".cxx", ".cpp", ".m", ".s", ".h", ".hh", ".hpp", ".hxx", ".S", ".swig", ".swigcxx":
+ case ".go", ".c", ".cc", ".cxx", ".cpp", ".m", ".s", ".h", ".hh", ".hpp", ".hxx", ".f", ".F", ".f90", ".S", ".swig", ".swigcxx":
// tentatively okay - read to make sure
case ".syso":
// binary, no reading
di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...)
case "CXXFLAGS":
di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...)
+ case "FFLAGS":
+ di.CgoFFLAGS = append(di.CgoFFLAGS, args...)
case "LDFLAGS":
di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...)
case "pkg-config":
// the result is safe for the shell.
func expandSrcDir(str string, srcdir string) (string, bool) {
// "\" delimited paths cause safeCgoName to fail
- // so convert native paths with a different delimeter
+ // so convert native paths with a different delimiter
// to "/" before starting (eg: on windows).
srcdir = filepath.ToSlash(srcdir)
t.Errorf("shouldBuild(file1) = false, want true")
}
if !reflect.DeepEqual(m, want1) {
- t.Errorf("shoudBuild(file1) tags = %v, want %v", m, want1)
+ t.Errorf("shouldBuild(file1) tags = %v, want %v", m, want1)
}
m = map[string]bool{}
if ctx.shouldBuild([]byte(file2), m) {
- t.Errorf("shouldBuild(file2) = true, want fakse")
+ t.Errorf("shouldBuild(file2) = true, want false")
}
if !reflect.DeepEqual(m, want2) {
- t.Errorf("shoudBuild(file2) tags = %v, want %v", m, want2)
+ t.Errorf("shouldBuild(file2) tags = %v, want %v", m, want2)
}
m = map[string]bool{}
t.Errorf("shouldBuild(file3) = false, want true")
}
if !reflect.DeepEqual(m, want3) {
- t.Errorf("shoudBuild(file3) tags = %v, want %v", m, want3)
+ t.Errorf("shouldBuild(file3) tags = %v, want %v", m, want3)
}
}
if string(line) == "!<arch>\n" {
// Archive file. Scan to __.PKGDEF.
var name string
- var size int
- if name, size, err = readGopackHeader(r); err != nil {
+ if name, _, err = readGopackHeader(r); err != nil {
return
}
- // Optional leading __.GOSYMDEF or __.SYMDEF.
- // Read and discard.
- if name == "__.SYMDEF" || name == "__.GOSYMDEF" {
- const block = 4096
- tmp := make([]byte, block)
- for size > 0 {
- n := size
- if n > block {
- n = block
- }
- if _, err = io.ReadFull(r, tmp[:n]); err != nil {
- return
- }
- size -= n
- }
-
- if name, _, err = readGopackHeader(r); err != nil {
- return
- }
- }
-
- // First real entry should be __.PKGDEF.
+ // First entry should be __.PKGDEF.
if name != "__.PKGDEF" {
err = errors.New("go archive is missing __.PKGDEF")
return
// For qualified names, the returned package is nil (and not created if
// it doesn't exist yet) unless materializePkg is set (which creates an
// unnamed package with valid package path). In the latter case, a
-// subequent import clause is expected to provide a name for the package.
+// subsequent import clause is expected to provide a name for the package.
//
func (p *parser) parseName(parent *types.Package, materializePkg bool) (pkg *types.Package, name string) {
pkg = parent
}
x := p.parseUnaryExpr(lhs)
- for _, prec := p.tokPrec(); prec >= prec1; prec-- {
- for {
- op, oprec := p.tokPrec()
- if oprec != prec {
- break
- }
- pos := p.expect(op)
- if lhs {
- p.resolve(x)
- lhs = false
- }
- y := p.parseBinaryExpr(false, prec+1)
- x = &ast.BinaryExpr{X: p.checkExpr(x), OpPos: pos, Op: op, Y: p.checkExpr(y)}
+ for {
+ op, oprec := p.tokPrec()
+ if oprec < prec1 {
+ return x
+ }
+ pos := p.expect(op)
+ if lhs {
+ p.resolve(x)
+ lhs = false
}
+ y := p.parseBinaryExpr(false, oprec+1)
+ x = &ast.BinaryExpr{X: p.checkExpr(x), OpPos: pos, Op: op, Y: p.checkExpr(y)}
}
-
- return x
}
// If lhs is set and the result is an identifier, it is not resolved.
} else {
// an untyped non-constant argument may appear if
// it contains a (yet untyped non-constant) shift
- // epression: convert it to complex128 which will
+ // expression: convert it to complex128 which will
// result in an error (shift of complex value)
check.convertUntyped(x, Typ[Complex128])
// x should be invalid now, but be conservative and check
type A [iota /* ERROR "cannot use iota" */ ]int
-// constant expressions with operands accross different
+// constant expressions with operands across different
// constant declarations must use the right iota values
const (
_c0 = iota
t.Fatal(err)
}
if len(clone.Templates()) != len(orig.Templates()) {
- t.Fatalf("Invalid lenth of t.Clone().Templates()")
+ t.Fatalf("Invalid length of t.Clone().Templates()")
}
const want = "stuff"
// for an image". In practice, though, giflib (a widely used C
// library) does not enforce this, so we also accept lzwr returning
// io.ErrUnexpectedEOF (meaning that the encoded stream hit io.EOF
- // before the LZW decoder saw an explict end code), provided that
+ // before the LZW decoder saw an explicit end code), provided that
// the io.ReadFull call above successfully read len(m.Pix) bytes.
// See https://golang.org/issue/9856 for an example GIF.
if n, err := lzwr.Read(d.tmp[:1]); n != 0 || (err != io.EOF && err != io.ErrUnexpectedEOF) {
--- /dev/null
+// Copyright 2016 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 unix
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+// getentropy(2)'s syscall number, from /usr/src/sys/kern/syscalls.master
+const entropyTrap uintptr = 7
+
+// GetEntropy calls the OpenBSD getentropy system call.
+func GetEntropy(p []byte) error {
+ _, _, errno := syscall.Syscall(entropyTrap,
+ uintptr(unsafe.Pointer(&p[0])),
+ uintptr(len(p)),
+ 0)
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
}
}
for n, v := range haveNames {
- t.Errorf("value %s (%v) is found while enumerating, but has not been cretaed", n, v)
+ t.Errorf("value %s (%v) is found while enumerating, but has not been created", n, v)
}
}
// read data with short buffer
gotsize, gottype, err = k.GetValue(test.Name, make([]byte, size-1))
if err == nil {
- t.Errorf("GetValue(%s, [%d]byte) should fail, but suceeded", test.Name, size-1)
+ t.Errorf("GetValue(%s, [%d]byte) should fail, but succeeded", test.Name, size-1)
return
}
if err != registry.ErrShortBuffer {
# This is used by cgo. Default is CXX, or, if that is not set,
# "g++" or "clang++".
#
+# FC: Command line to run to compile Fortran code for GOARCH.
+# This is used by cgo. Default is "gfortran".
+#
# GO_DISTFLAGS: extra flags to provide to "dist bootstrap".
set -e
::
:: CC_FOR_TARGET: Command line to run compile C code for GOARCH.
:: This is used by cgo. Default is CC.
+::
+:: FC: Command line to run to compile Fortran code.
+:: This is used by cgo. Default is "gfortran".
@echo off
}
if x.form == finite && y.form == finite {
- // x + y (commom case)
+ // x + y (common case)
z.neg = x.neg
if x.neg == y.neg {
// x + y == x + y
if fcount < 0 {
// The mantissa has a "decimal" point ddd.dddd; and
// -fcount is the number of digits to the right of '.'.
- // Adjust relevant exponent accodingly.
+ // Adjust relevant exponent accordingly.
d := int64(fcount)
switch b {
case 10:
return ok
}
-// absDomainName returns an absoulte domain name which ends with a
+// absDomainName returns an absolute domain name which ends with a
// trailing dot to match pure Go reverse resolver and all other lookup
// routines.
// See golang.org/issue/12189.
// Name resolution APIs and libraries should recognize the
// followings as special and should not send any queries.
- // Though, we test those names here for verifying nagative
+ // Though, we test those names here for verifying negative
// answers at DNS query-response interaction level.
{"localhost.", dnsTypeALL, dnsRcodeNameError},
{"invalid.", dnsTypeALL, dnsRcodeNameError},
}
addrs, err := goLookupIP(tt.name)
if err != nil {
- if err, ok := err.(*DNSError); !ok || (err.Name != tt.error.(*DNSError).Name || err.Server != tt.error.(*DNSError).Server || err.IsTimeout != tt.error.(*DNSError).IsTimeout) {
+ // This test uses external network connectivity.
+ // We need to take care with errors on both
+ // DNS message exchange layer and DNS
+ // transport layer because goLookupIP may fail
+ // when the IP connectivty on node under test
+ // gets lost during its run.
+ if err, ok := err.(*DNSError); !ok || tt.error != nil && (err.Name != tt.error.(*DNSError).Name || err.Server != tt.error.(*DNSError).Server || err.IsTimeout != tt.error.(*DNSError).IsTimeout) {
t.Errorf("got %v; want %v", err, tt.error)
}
continue
for _, order := range []hostLookupOrder{hostLookupFilesDNS, hostLookupDNSFiles} {
name := fmt.Sprintf("order %v", order)
- // First ensure that we get an error when contacting a non-existant host.
+ // First ensure that we get an error when contacting a non-existent host.
_, err := goLookupIPOrder("notarealhost", order)
if err == nil {
t.Errorf("%s: expected error while looking up name not in hosts file", name)
}
func setDeadlineImpl(fd *netFD, t time.Time, mode int) error {
- d := runtimeNano() + int64(t.Sub(time.Now()))
+ diff := int64(t.Sub(time.Now()))
+ d := runtimeNano() + diff
+ if d <= 0 && diff > 0 {
+ // If the user has a deadline in the future, but the delay calculation
+ // overflows, then set the deadline to the maximum possible value.
+ d = 1<<63 - 1
+ }
if t.IsZero() {
d = 0
}
t.Fatal("didn't see redirect")
}
if lastReq.Cancel != cancel {
- t.Errorf("expected lastReq to have the cancel channel set on the inital req")
+ t.Errorf("expected lastReq to have the cancel channel set on the initial req")
}
checkErr = errors.New("no redirects allowed")
return cookies
}
-// validCookieDomain returns wheter v is a valid cookie domain-value.
+// validCookieDomain returns whether v is a valid cookie domain-value.
func validCookieDomain(v string) bool {
if isCookieDomainName(v) {
return true
}
// registerHTTPSProtocol calls Transport.RegisterProtocol but
-// convering panics into errors.
+// converting panics into errors.
func http2registerHTTPSProtocol(t *Transport, rt RoundTripper) (err error) {
defer func() {
if e := recover(); e != nil {
http2PriorityParam
}
-// PriorityParam are the stream prioritzation parameters.
+// PriorityParam are the stream prioritization parameters.
type http2PriorityParam struct {
// StreamDep is a 31-bit stream identifier for the
// stream that this stream depends on. Zero means no
weight uint8
state http2streamState
sentReset bool // only true once detached from streams map
- gotReset bool // only true once detacted from streams map
+ gotReset bool // only true once detached from streams map
gotTrailerHeader bool // HEADER frame for trailers was seen
trailer Header // accumulated trailers
// trailers. That worked for a while, until we found the first major
// user of Trailers in the wild: gRPC (using them only over http2),
// and gRPC libraries permit setting trailers mid-stream without
-// predeclarnig them. So: change of plans. We still permit the old
+// predeclaring them. So: change of plans. We still permit the old
// way, but we also permit this hack: if a Header() key begins with
// "Trailer:", the suffix of that key is a Trailer. Because ':' is an
// invalid token byte anyway, there is no ambiguity. (And it's already
// send in the initial settings frame. It is how many bytes
// of response headers are allow. Unlike the http2 spec, zero here
// means to use a default limit (currently 10MB). If you actually
- // want to advertise an ulimited value to the peer, Transport
+ // want to advertise an unlimited value to the peer, Transport
// interprets the highest possible value here (0xffffffff or 1<<32-1)
// to mean no limit.
MaxHeaderListSize uint32
// frameBuffer returns a scratch buffer suitable for writing DATA frames.
// They're capped at the min of the peer's max frame size or 512KB
// (kinda arbitrarily), but definitely capped so we don't allocate 4GB
-// bufers.
+// buffers.
func (cc *http2ClientConn) frameScratchBuffer() []byte {
cc.mu.Lock()
size := cc.maxFrameSize
return ws.takeFrom(q.streamID(), q)
}
-// zeroCanSend is defered from take.
+// zeroCanSend is deferred from take.
func (ws *http2writeScheduler) zeroCanSend() {
for i := range ws.canSend {
ws.canSend[i] = nil
res, err = http.Get(ts.URL)
if err == nil {
body, _ := ioutil.ReadAll(res.Body)
- t.Fatalf("Unexected response after close: %v, %v, %s", res.Status, res.Header, body)
+ t.Fatalf("Unexpected response after close: %v, %v, %s", res.Status, res.Header, body)
}
}
return in
}
-// removeZone removes IPv6 zone identifer from host.
+// removeZone removes IPv6 zone identifier from host.
// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
func removeZone(host string) string {
if !strings.HasPrefix(host, "[") {
var parseContentTypeTests = []parseContentTypeTest{
{false, stringMap{"Content-Type": {"text/plain"}}},
- // Empty content type is legal - shoult be treated as
+ // Empty content type is legal - should be treated as
// application/octet-stream (RFC 2616, section 7.2.1)
{false, stringMap{}},
{true, stringMap{"Content-Type": {"text/plain; boundary="}}},
"Transfer-Encoding: chunked\r\n\r\n" +
// TODO: currently we don't buffer before chunking, so we get a
// single "m" chunk before the other chunks, as this was the 1-byte
- // read from our MultiReader where we stiched the Body back together
+ // read from our MultiReader where we stitched the Body back together
// after sniffing whether the Body was 0 bytes or not.
chunk("m") +
chunk("y body") +
// badRequestError is a literal string (used by in the server in HTML,
// unescaped) to tell the user why their request was bad. It should
-// be plain text without user info or other embeddded errors.
+// be plain text without user info or other embedded errors.
type badRequestError string
func (e badRequestError) Error() string { return "Bad Request: " + string(e) }
func shouldClose(major, minor int, header Header, removeCloseHeader bool) bool {
if major < 1 {
return true
- } else if major == 1 && minor == 0 {
- vv := header["Connection"]
- if headerValuesContainsToken(vv, "close") || !headerValuesContainsToken(vv, "keep-alive") {
- return true
- }
- return false
- } else {
- if headerValuesContainsToken(header["Connection"], "close") {
- if removeCloseHeader {
- header.Del("Connection")
- }
- return true
- }
}
- return false
+
+ conv := header["Connection"]
+ hasClose := headerValuesContainsToken(conv, "close")
+ if major == 1 && minor == 0 {
+ return hasClose || !headerValuesContainsToken(conv, "keep-alive")
+ }
+
+ if hasClose && removeCloseHeader {
+ header.Del("Connection")
+ }
+
+ return hasClose
}
// Parse the trailer header
// (but may be used for non-keep-alive requests as well)
type persistConn struct {
// alt optionally specifies the TLS NextProto RoundTripper.
- // This is used for HTTP/2 today and future protocol laters.
+ // This is used for HTTP/2 today and future protocols later.
// If it's non-nil, the rest of the fields are unused.
alt RoundTripper
{path: "/100", body: []byte("hello"), sent: 5, status: 200}, // Got 100 followed by 200, entire body is sent.
{path: "/200", body: []byte("hello"), sent: 0, status: 200}, // Got 200 without 100. body isn't sent.
{path: "/500", body: []byte("hello"), sent: 0, status: 500}, // Got 500 without 100. body isn't sent.
- {path: "/keepalive", body: []byte("hello"), sent: 0, status: 500}, // Althogh without Connection:close, body isn't sent.
+ {path: "/keepalive", body: []byte("hello"), sent: 0, status: 500}, // Although without Connection:close, body isn't sent.
{path: "/timeout", body: []byte("hello"), sent: 5, status: 200}, // Timeout exceeded and entire body is sent.
}
case *syscall.SockaddrInet6:
ifa.IP = make(IP, IPv6len)
copy(ifa.IP, sa.Addr[:])
- // NOTE: KAME based IPv6 protcol stack usually embeds
+ // NOTE: KAME based IPv6 protocol stack usually embeds
// the interface index in the interface-local or
// link-local address as the kernel-internal form.
if ifa.IP.IsLinkLocalUnicast() {
case *syscall.SockaddrInet6:
ifma := IPAddr{IP: make(IP, IPv6len)}
copy(ifma.IP, sa.Addr[:])
- // NOTE: KAME based IPv6 protcol stack usually embeds
+ // NOTE: KAME based IPv6 protocol stack usually embeds
// the interface index in the interface-local or
// link-local address as the kernel-internal form.
if ifma.IP.IsInterfaceLocalMulticast() || ifma.IP.IsLinkLocalMulticast() {
case *syscall.SockaddrInet6:
ifma := IPAddr{IP: make(IP, IPv6len)}
copy(ifma.IP, sa.Addr[:])
- // NOTE: KAME based IPv6 protcol stack usually embeds
+ // NOTE: KAME based IPv6 protocol stack usually embeds
// the interface index in the interface-local or
// link-local address as the kernel-internal form.
if ifma.IP.IsInterfaceLocalMulticast() || ifma.IP.IsLinkLocalMulticast() {
}
// Test the existence of connected unicast routes for
// IPv6. We can assume the existence of ::1/128 when
- // at least one looopback interface is installed.
+ // at least one loopback interface is installed.
if supportsIPv6 && stats.loop > 0 && stats.uni6 == 0 {
t.Errorf("num IPv6 unicast routes = 0; want >0; summary: %+v", stats)
}
}
// Test the existence of connected unicast routes for IPv6.
// We can assume the existence of ::1/128 when at least one
- // looopback interface is installed.
+ // loopback interface is installed.
if supportsIPv6 && stats.loop > 0 && stats.uni6 == 0 {
t.Errorf("num IPv6 unicast routes = 0; want >0; summary: %+v", stats)
}
for i := 0; i < 3; i++ {
ti := &testInterface{local: local, remote: remote}
if err := ti.setPointToPoint(5963 + i); err != nil {
- t.Skipf("test requries external command: %v", err)
+ t.Skipf("test requires external command: %v", err)
}
if err := ti.setup(); err != nil {
t.Fatal(err)
{"", "0", ":0"},
{"google.com", "https%foo", "google.com:https%foo"}, // Go 1.0 behavior
- {"127.0.0.1", "", "127.0.0.1:"}, // Go 1.0 behaviour
- {"www.google.com", "", "www.google.com:"}, // Go 1.0 behaviour
+ {"127.0.0.1", "", "127.0.0.1:"}, // Go 1.0 behavior
+ {"www.google.com", "", "www.google.com:"}, // Go 1.0 behavior
}
var splitFailureTests = []struct {
// Some released versions of DragonFly BSD pretend to
// accept IPV6_V6ONLY=0 successfully, but the state
// still stays IPV6_V6ONLY=1. Eventually DragonFly BSD
- // stops preteding, but the transition period would
+ // stops pretending, but the transition period would
// cause unpredictable behavior and we need to avoid
// it.
//
network2, address2 string // second listener
xerr error // expected error value, nil or other
}{
- // Test cases and expected results for the attemping 2nd listen on the same port
+ // Test cases and expected results for the attempting 2nd listen on the same port
// 1st listen 2nd listen darwin freebsd linux openbsd
// ------------------------------------------------------------------------------------
// "tcp" "" "tcp" "" - - - -
// TestDualStackTCPListener tests both single and double listen
// to a test listener with various address families, different
// listening address and same port.
+//
+// On DragonFly BSD, we expect the kernel version of node under test
+// to be greater than or equal to 4.4.
func TestDualStackTCPListener(t *testing.T) {
switch runtime.GOOS {
- case "dragonfly", "nacl", "plan9": // re-enable on dragonfly once the new IP control block management has landed
+ case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if !supportsIPv4 || !supportsIPv6 {
}
// TestDualStackUDPListener tests both single and double listen
-// to a test listener with various address families, differnet
+// to a test listener with various address families, different
// listening address and same port.
+//
+// On DragonFly BSD, we expect the kernel version of node under test
+// to be greater than or equal to 4.4.
func TestDualStackUDPListener(t *testing.T) {
switch runtime.GOOS {
- case "dragonfly", "nacl", "plan9": // re-enable on dragonfly once the new IP control block management has landed
+ case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if !supportsIPv4 || !supportsIPv6 {
--- /dev/null
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !nacl,!plan9,!windows
+
+package net
+
+// forceGoDNS forces the resolver configuration to use the pure Go resolver
+// and returns a fixup function to restore the old settings.
+func forceGoDNS() func() {
+ c := systemConf()
+ oldGo := c.netGo
+ oldCgo := c.netCgo
+ fixup := func() {
+ c.netGo = oldGo
+ c.netCgo = oldCgo
+ }
+ c.netGo = true
+ c.netCgo = false
+ return fixup
+}
+
+// forceCgoDNS forces the resolver configuration to use the cgo resolver
+// and returns a fixup function to restore the old settings.
+// (On non-Unix systems forceCgoDNS returns nil.)
+func forceCgoDNS() func() {
+ c := systemConf()
+ oldGo := c.netGo
+ oldCgo := c.netCgo
+ fixup := func() {
+ c.netGo = oldGo
+ c.netCgo = oldCgo
+ }
+ c.netGo = false
+ c.netCgo = true
+ return fixup
+}
import "runtime"
-// See unix_test.go for what these (don't) do.
+// See main_conf_test.go for what these (don't) do.
func forceGoDNS() func() {
switch runtime.GOOS {
case "plan9", "windows":
}
}
-// See unix_test.go for what these (don't) do.
+// See main_conf_test.go for what these (don't) do.
func forceCgoDNS() func() { return nil }
}
func netshInterfaceIPv4ShowAddress(name string, netshOutput []byte) []string {
- // adress information is listed like:
+ // Address information is listed like:
//
//Configuration for interface "Local Area Connection"
// DHCP enabled: Yes
}
func netshInterfaceIPv6ShowAddress(name string, netshOutput []byte) []string {
- // adress information is listed like:
+ // Address information is listed like:
//
//Address ::1 Parameters
//---------------------------------------------------------
//
// if handled == false, sendFile performed no work.
//
-// Note that sendfile for windows does not suppport >2GB file.
+// Note that sendfile for windows does not support >2GB file.
func sendFile(fd *netFD, r io.Reader) (written int64, err error, handled bool) {
var n int64 = 0 // by default, copy until EOF
const toLower = 'a' - 'A'
// validHeaderFieldByte reports whether b is a valid byte in a header
-// field key. This is actually stricter than RFC 7230, which says:
+// field name. RFC 7230 says:
+// header-field = field-name ":" OWS field-value OWS
+// field-name = token
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
// token = 1*tchar
-// TODO: revisit in Go 1.6+ and possibly expand this. But note that many
-// servers have historically dropped '_' to prevent ambiguities when mapping
-// to CGI environment variables.
func validHeaderFieldByte(b byte) bool {
- return ('A' <= b && b <= 'Z') ||
- ('a' <= b && b <= 'z') ||
- ('0' <= b && b <= '9') ||
- b == '-'
+ return int(b) < len(isTokenTable) && isTokenTable[b]
}
// canonicalMIMEHeaderKey is like CanonicalMIMEHeaderKey but is
commonHeader[v] = v
}
}
+
+// isTokenTable is a copy of net/http/lex.go's isTokenTable.
+// See https://httpwg.github.io/specs/rfc7230.html#rule.token.separators
+var isTokenTable = [127]bool{
+ '!': true,
+ '#': true,
+ '$': true,
+ '%': true,
+ '&': true,
+ '\'': true,
+ '*': true,
+ '+': true,
+ '-': true,
+ '.': true,
+ '0': true,
+ '1': true,
+ '2': true,
+ '3': true,
+ '4': true,
+ '5': true,
+ '6': true,
+ '7': true,
+ '8': true,
+ '9': true,
+ 'A': true,
+ 'B': true,
+ 'C': true,
+ 'D': true,
+ 'E': true,
+ 'F': true,
+ 'G': true,
+ 'H': true,
+ 'I': true,
+ 'J': true,
+ 'K': true,
+ 'L': true,
+ 'M': true,
+ 'N': true,
+ 'O': true,
+ 'P': true,
+ 'Q': true,
+ 'R': true,
+ 'S': true,
+ 'T': true,
+ 'U': true,
+ 'W': true,
+ 'V': true,
+ 'X': true,
+ 'Y': true,
+ 'Z': true,
+ '^': true,
+ '_': true,
+ '`': true,
+ 'a': true,
+ 'b': true,
+ 'c': true,
+ 'd': true,
+ 'e': true,
+ 'f': true,
+ 'g': true,
+ 'h': true,
+ 'i': true,
+ 'j': true,
+ 'k': true,
+ 'l': true,
+ 'm': true,
+ 'n': true,
+ 'o': true,
+ 'p': true,
+ 'q': true,
+ 'r': true,
+ 's': true,
+ 't': true,
+ 'u': true,
+ 'v': true,
+ 'w': true,
+ 'x': true,
+ 'y': true,
+ 'z': true,
+ '|': true,
+ '~': true,
+}
{"user-agent", "User-Agent"},
{"USER-AGENT", "User-Agent"},
+ // Other valid tchar bytes in tokens:
+ {"foo-bar_baz", "Foo-Bar_baz"},
+ {"foo-bar$baz", "Foo-Bar$baz"},
+ {"foo-bar~baz", "Foo-Bar~baz"},
+ {"foo-bar*baz", "Foo-Bar*baz"},
+
// Non-ASCII or anything with spaces or non-token chars is unchanged:
{"üser-agenT", "üser-agenT"},
{"a B", "a B"},
{-5 * time.Second, 0, -5 * time.Second, 100 * time.Millisecond},
{0, -5 * time.Second, -5 * time.Second, 100 * time.Millisecond},
{-5 * time.Second, 5 * time.Second, -5 * time.Second, 100 * time.Millisecond}, // timeout over deadline
+ {-1 << 63, 0, time.Second, 100 * time.Millisecond},
+ {0, -1 << 63, time.Second, 100 * time.Millisecond},
{50 * time.Millisecond, 0, 100 * time.Millisecond, time.Second},
{0, 50 * time.Millisecond, 100 * time.Millisecond, time.Second},
}
}
+var dialTimeoutMaxDurationTests = []struct {
+ timeout time.Duration
+ delta time.Duration // for deadline
+}{
+ // Large timeouts that will overflow an int64 unix nanos.
+ {1<<63 - 1, 0},
+ {0, 1<<63 - 1},
+}
+
+func TestDialTimeoutMaxDuration(t *testing.T) {
+ t.Parallel()
+
+ ln, err := newLocalListener("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ for i, tt := range dialTimeoutMaxDurationTests {
+ ch := make(chan error)
+ max := time.NewTimer(100 * time.Millisecond)
+ defer max.Stop()
+ go func() {
+ d := Dialer{Timeout: tt.timeout}
+ if tt.delta != 0 {
+ d.Deadline = time.Now().Add(tt.delta)
+ }
+ c, err := d.Dial(ln.Addr().Network(), ln.Addr().String())
+ if err == nil {
+ c.Close()
+ }
+ ch <- err
+ }()
+
+ select {
+ case <-max.C:
+ t.Fatalf("#%d: Dial didn't return in an expected time", i)
+ case err := <-ch:
+ if perr := parseDialError(err); perr != nil {
+ t.Error(perr)
+ }
+ if err != nil {
+ t.Errorf("#%d: %v", i, err)
+ }
+ }
+ }
+}
+
var acceptTimeoutTests = []struct {
timeout time.Duration
xerrs [2]error // expected errors in transition
switch err {
case nil: // ReadFrom succeeds
default: // Read may timeout, it depends on the platform
- if nerr, ok := err.(Error); (!ok || !nerr.Timeout()) && runtime.GOOS != "windows" { // Windows retruns WSAEMSGSIZ
+ if nerr, ok := err.(Error); (!ok || !nerr.Timeout()) && runtime.GOOS != "windows" { // Windows returns WSAEMSGSIZ
t.Fatal(err)
}
}
t.Fatal("closing unix listener did not remove unix socket")
}
}
-
-// forceGoDNS forces the resolver configuration to use the pure Go resolver
-// and returns a fixup function to restore the old settings.
-func forceGoDNS() func() {
- c := systemConf()
- oldGo := c.netGo
- oldCgo := c.netCgo
- fixup := func() {
- c.netGo = oldGo
- c.netCgo = oldCgo
- }
- c.netGo = true
- c.netCgo = false
- return fixup
-}
-
-// forceCgoDNS forces the resolver configuration to use the cgo resolver
-// and returns a fixup function to restore the old settings.
-// (On non-Unix systems forceCgoDNS returns nil.)
-func forceCgoDNS() func() {
- c := systemConf()
- oldGo := c.netGo
- oldCgo := c.netCgo
- fixup := func() {
- c.netGo = oldGo
- c.netCgo = oldCgo
- }
- c.netGo = false
- c.netCgo = true
- return fixup
-}
// construct a URL struct directly and set the Opaque field instead of Path.
// These still work as well.
type URL struct {
- Scheme string
- Opaque string // encoded opaque data
- User *Userinfo // username and password information
- Host string // host or host:port
- Path string
- RawPath string // encoded path hint (Go 1.5 and later only; see EscapedPath method)
- RawQuery string // encoded query values, without '?'
- Fragment string // fragment for references, without '#'
+ Scheme string
+ Opaque string // encoded opaque data
+ User *Userinfo // username and password information
+ Host string // host or host:port
+ Path string
+ RawPath string // encoded path hint (Go 1.5 and later only; see EscapedPath method)
+ ForceQuery bool // append a query ('?') even if RawQuery is empty
+ RawQuery string // encoded query values, without '?'
+ Fragment string // fragment for references, without '#'
}
// User returns a Userinfo containing the provided username
}
url.Scheme = strings.ToLower(url.Scheme)
- rest, url.RawQuery = split(rest, "?", true)
+ if strings.HasSuffix(rest, "?") {
+ url.ForceQuery = true
+ rest = rest[:len(rest)-1]
+ } else {
+ rest, url.RawQuery = split(rest, "?", true)
+ }
if !strings.HasPrefix(rest, "/") {
if url.Scheme != "" {
}
buf.WriteString(path)
}
- if u.RawQuery != "" {
+ if u.ForceQuery || u.RawQuery != "" {
buf.WriteByte('?')
buf.WriteString(u.RawQuery)
}
result = u.Scheme + ":" + result
}
}
- if u.RawQuery != "" {
+ if u.ForceQuery || u.RawQuery != "" {
result += "?" + u.RawQuery
}
return result
},
"ftp://john%20doe@www.google.com/",
},
+ // empty query
+ {
+ "http://www.google.com/?",
+ &URL{
+ Scheme: "http",
+ Host: "www.google.com",
+ Path: "/",
+ ForceQuery: true,
+ },
+ "",
+ },
// query
{
"http://www.google.com/?q=go+language",
// Absolute URL references
{"http://foo.com?a=b", "https://bar.com/", "https://bar.com/"},
{"http://foo.com/", "https://bar.com/?a=b", "https://bar.com/?a=b"},
+ {"http://foo.com/", "https://bar.com/?", "https://bar.com/?"},
{"http://foo.com/bar", "mailto:foo@example.com", "mailto:foo@example.com"},
// Path-absolute references
{"http://foo.com/bar", "/baz", "http://foo.com/baz"},
{"http://foo.com/bar?a=b#f", "/baz", "http://foo.com/baz"},
+ {"http://foo.com/bar?a=b", "/baz?", "http://foo.com/baz?"},
{"http://foo.com/bar?a=b", "/baz?c=d", "http://foo.com/baz?c=d"},
// Scheme-relative
},
"//foo",
},
+ {
+ &URL{
+ Scheme: "http",
+ Host: "example.com",
+ Path: "/foo",
+ ForceQuery: true,
+ },
+ "/foo?",
+ },
}
func TestRequestURI(t *testing.T) {
//
// We want to test that FDs in the child do not get overwritten
// by one another as this shuffle occurs. The original implementation
- // was buggy in that in some data dependent cases it would ovewrite
+ // was buggy in that in some data dependent cases it would overwrite
// stderr in the child with one of the ExtraFile members.
// Testing for this case is difficult because it relies on using
// the same FD values as that case. In particular, an FD of 3
}
// createFiles copies srcPath file into multiply files.
-// It uses dir as preifx for all destination files.
+// It uses dir as prefix for all destination files.
func createFiles(t *testing.T, dir string, files []string, srcPath string) {
for _, f := range files {
installProg(t, filepath.Join(dir, f), srcPath)
},
{
// LookPath(`a.exe`) will find `.\a.exe`, but prefixing that with
- // dir `p\a.exe` will refer to not existant file
+ // dir `p\a.exe` will refer to a non-existent file
files: []string{`a.exe`, `p\not_important_file`},
dir: `p`,
arg0: `a.exe`,
},
{
// like above, but making test succeed by installing file
- // in refered destination (so LookPath(`a.exe`) will still
+ // in referred destination (so LookPath(`a.exe`) will still
// find `.\a.exe`, but we successfully execute `p\a.exe`)
files: []string{`a.exe`, `p\a.exe`},
dir: `p`,
switch runtime.GOOS {
case "android":
return &sysDir{
- "/system/framework",
+ "/system/lib",
[]string{
- "ext.jar",
- "framework.jar",
+ "libmedia.so",
+ "libpowermanager.so",
},
}
case "darwin":
return s
}
- if got, want := names(mustReadDir("inital readdir")),
+ if got, want := names(mustReadDir("initial readdir")),
[]string{"good1", "good2", "x"}; !reflect.DeepEqual(got, want) {
t.Errorf("initial readdir got %q; want %q", got, want)
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Simple converions to avoid depending on strconv.
+// Simple conversions to avoid depending on strconv.
package os
if err == nil {
return name, nil
}
- // domain worked neigher as a domain nor as a server
+ // domain worked neither as a domain nor as a server
// could be domain server unavailable
// pretend username is fullname
return username, nil
// joinNonEmpty is like join, but it assumes that the first element is non-empty.
func joinNonEmpty(elem []string) string {
if len(elem[0]) == 2 && elem[0][1] == ':' {
- // First element is drive leter without terminating slash.
+ // First element is drive letter without terminating slash.
// Keep path relative to current directory on that drive.
return Clean(elem[0] + strings.Join(elem[1:], string(Separator)))
}
// off the stack into the frame will store an *Inner there, and then if a garbage collection
// happens to scan that argument frame before it is discarded, it will scan the *Inner
// memory as if it were an *Outer. If the two have different memory layouts, the
-// collection will intepret the memory incorrectly.
+// collection will interpret the memory incorrectly.
//
// One such possible incorrect interpretation is to treat two arbitrary memory words
// (Inner.P1 and Inner.P2 below) as an interface (Outer.R below). Because interpreting
// a limitation of escape analysis. If that is ever fixed the
// allocs < 0.5 condition will trigger and this test should be fixed.
}
+
+type nameTest struct {
+ v interface{}
+ want string
+}
+
+var nameTests = []nameTest{
+ {int32(0), "int32"},
+ {D1{}, "D1"},
+ {[]D1{}, ""},
+ {(chan D1)(nil), ""},
+ {(func() D1)(nil), ""},
+}
+
+func TestNames(t *testing.T) {
+ for _, test := range nameTests {
+ if got := TypeOf(test.v).Name(); got != test.want {
+ t.Errorf("%T Name()=%q, want %q", test.v, got, test.want)
+ }
+ }
+}
var r []string
for _, m := range typelinks() {
for _, t := range m {
- r = append(r, *t.string)
+ r = append(r, t.string)
}
}
return r
kind uint8 // enumeration for C
alg *typeAlg // algorithm table
gcdata *byte // garbage collection data
- string *string // string form; unnecessary but undeniably useful
+ string string // string form; unnecessary but undeniably useful
*uncommonType // (relatively) uncommon fields
- ptrToThis *rtype // type for pointer to this type, if used in binary or has methods
}
// a copy of runtime.typeAlg
// Using a pointer to this struct reduces the overall size required
// to describe an unnamed type with no methods.
type uncommonType struct {
- name *string // name of type
pkgPath *string // import path; nil for built-in types like int, string
methods []method // methods associated with type
}
return *t.pkgPath
}
-func (t *uncommonType) Name() string {
- if t == nil || t.name == nil {
- return ""
- }
- return *t.name
-}
-
-func (t *rtype) String() string { return *t.string }
+func (t *rtype) String() string { return t.string }
func (t *rtype) Size() uintptr { return t.size }
return t.uncommonType.PkgPath()
}
+func hasPrefix(s, prefix string) bool {
+ return len(s) >= len(prefix) && s[:len(prefix)] == prefix
+}
+
func (t *rtype) Name() string {
- return t.uncommonType.Name()
+ if hasPrefix(t.string, "map[") {
+ return ""
+ }
+ if hasPrefix(t.string, "struct {") {
+ return ""
+ }
+ if hasPrefix(t.string, "chan ") {
+ return ""
+ }
+ if hasPrefix(t.string, "func(") {
+ return ""
+ }
+ if t.string[0] == '[' || t.string[0] == '*' {
+ return ""
+ }
+ i := len(t.string) - 1
+ for i >= 0 {
+ if t.string[i] == '.' {
+ break
+ }
+ i--
+ }
+ return t.string[i+1:]
}
func (t *rtype) ChanDir() ChanDir {
}
func (t *rtype) ptrTo() *rtype {
- if p := t.ptrToThis; p != nil {
- return p
- }
-
- // Otherwise, synthesize one.
- // This only happens for pointers with no methods.
- // We keep the mapping in a map on the side, because
- // this operation is rare and a separate map lets us keep
- // the type structures in read-only memory.
+ // Check the cache.
ptrMap.RLock()
if m := ptrMap.m; m != nil {
if p := m[t]; p != nil {
}
}
ptrMap.RUnlock()
+
ptrMap.Lock()
if ptrMap.m == nil {
ptrMap.m = make(map[*rtype]*ptrType)
}
// Look in known types.
- s := "*" + *t.string
+ s := "*" + t.string
for _, tt := range typesByString(s) {
p = (*ptrType)(unsafe.Pointer(tt))
if p.elem == t {
prototype := *(**ptrType)(unsafe.Pointer(&iptr))
*p = *prototype
- p.string = &s
+ p.string = s
// For the type structures linked into the binary, the
// compiler provides a good hash of the string.
p.hash = fnv1(t.hash, '*')
p.uncommonType = nil
- p.ptrToThis = nil
p.elem = t
ptrMap.m[t] = p
// Note that strings are not unique identifiers for types:
// there can be more than one with a given string.
// Only types we might want to look up are included:
-// channels, maps, slices, and arrays.
+// pointers, channels, maps, slices, and arrays.
func typelinks() [][]*rtype
// typesByString returns the subslice of typelinks() whose elements have
for i < j {
h := i + (j-i)/2 // avoid overflow when computing h
// i ≤ h < j
- if !(*typ[h].string >= s) {
+ if !(typ[h].string >= s) {
i = h + 1 // preserves f(i-1) == false
} else {
j = h // preserves f(j) == true
// We could do a second binary search, but the caller is going
// to do a linear scan anyway.
j = i
- for j < len(typ) && *typ[j].string == s {
+ for j < len(typ) && typ[j].string == s {
j++
}
lookupCache.Unlock()
panic("reflect.ChanOf: invalid dir")
case SendDir:
- s = "chan<- " + *typ.string
+ s = "chan<- " + typ.string
case RecvDir:
- s = "<-chan " + *typ.string
+ s = "<-chan " + typ.string
case BothDir:
- s = "chan " + *typ.string
+ s = "chan " + typ.string
}
for _, tt := range typesByString(s) {
ch := (*chanType)(unsafe.Pointer(tt))
ch := new(chanType)
*ch = *prototype
ch.dir = uintptr(dir)
- ch.string = &s
+ ch.string = s
ch.hash = fnv1(typ.hash, 'c', byte(dir))
ch.elem = typ
ch.uncommonType = nil
- ch.ptrToThis = nil
return cachePut(ckey, &ch.rtype)
}
}
// Look in known types.
- s := "map[" + *ktyp.string + "]" + *etyp.string
+ s := "map[" + ktyp.string + "]" + etyp.string
for _, tt := range typesByString(s) {
mt := (*mapType)(unsafe.Pointer(tt))
if mt.key == ktyp && mt.elem == etyp {
var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil)
mt := new(mapType)
*mt = **(**mapType)(unsafe.Pointer(&imap))
- mt.string = &s
+ mt.string = s
mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash))
mt.key = ktyp
mt.elem = etyp
mt.reflexivekey = isReflexive(ktyp)
mt.needkeyupdate = needKeyUpdate(ktyp)
mt.uncommonType = nil
- mt.ptrToThis = nil
return cachePut(ckey, &mt.rtype)
}
}
// Populate the remaining fields of ft and store in cache.
- ft.string = &str
+ ft.string = str
ft.uncommonType = nil
- ft.ptrToThis = nil
funcLookupCache.m[hash] = append(funcLookupCache.m[hash], &ft.rtype)
return &ft.rtype
}
if ft.dotdotdot && i == len(ft.in)-1 {
repr = append(repr, "..."...)
- repr = append(repr, *(*sliceType)(unsafe.Pointer(t)).elem.string...)
+ repr = append(repr, (*sliceType)(unsafe.Pointer(t)).elem.string...)
} else {
- repr = append(repr, *t.string...)
+ repr = append(repr, t.string...)
}
}
repr = append(repr, ')')
if i > 0 {
repr = append(repr, ", "...)
}
- repr = append(repr, *t.string...)
+ repr = append(repr, t.string...)
}
if len(ft.out) > 1 {
repr = append(repr, ')')
b.ptrdata = ptrdata
b.kind = kind
b.gcdata = gcdata
- s := "bucket(" + *ktyp.string + "," + *etyp.string + ")"
- b.string = &s
+ s := "bucket(" + ktyp.string + "," + etyp.string + ")"
+ b.string = s
return b
}
}
// Look in known types.
- s := "[]" + *typ.string
+ s := "[]" + typ.string
for _, tt := range typesByString(s) {
slice := (*sliceType)(unsafe.Pointer(tt))
if slice.elem == typ {
prototype := *(**sliceType)(unsafe.Pointer(&islice))
slice := new(sliceType)
*slice = *prototype
- slice.string = &s
+ slice.string = s
slice.hash = fnv1(typ.hash, '[')
slice.elem = typ
slice.uncommonType = nil
- slice.ptrToThis = nil
return cachePut(ckey, &slice.rtype)
}
}
// Look in known types.
- s := "[" + strconv.Itoa(count) + "]" + *typ.string
+ s := "[" + strconv.Itoa(count) + "]" + typ.string
for _, tt := range typesByString(s) {
array := (*arrayType)(unsafe.Pointer(tt))
if array.elem == typ {
prototype := *(**arrayType)(unsafe.Pointer(&iarray))
array := new(arrayType)
*array = *prototype
- array.string = &s
+ array.string = s
array.hash = fnv1(typ.hash, '[')
for n := uint32(count); n > 0; n >>= 8 {
array.hash = fnv1(array.hash, byte(n))
array.align = typ.align
array.fieldAlign = typ.fieldAlign
array.uncommonType = nil
- array.ptrToThis = nil
array.len = uintptr(count)
array.slice = slice.(*rtype)
var s string
if rcvr != nil {
- s = "methodargs(" + *rcvr.string + ")(" + *t.string + ")"
+ s = "methodargs(" + rcvr.string + ")(" + t.string + ")"
} else {
- s = "funcargs(" + *t.string + ")"
+ s = "funcargs(" + t.string + ")"
}
- x.string = &s
+ x.string = s
// cache result for future callers
if layoutCache.m == nil {
// h REG_MULTIREF multiple digit backref
// i REG_ICASE ignore case
// j REG_SPAN . matches \n
- // k REG_ESCAPE \ to ecape [...] delimiter
+ // k REG_ESCAPE \ to escape [...] delimiter
// l REG_LEFT implicit ^...
// m REG_MINIMAL minimal match
// n REG_NEWLINE explicit \n match
t := tab._type
fn := t.alg.hash
if fn == nil {
- panic(errorString("hash of unhashable type " + *t._string))
+ panic(errorString("hash of unhashable type " + t._string))
}
if isDirectIface(t) {
return c1 * fn(unsafe.Pointer(&a.data), h^c0)
}
fn := t.alg.hash
if fn == nil {
- panic(errorString("hash of unhashable type " + *t._string))
+ panic(errorString("hash of unhashable type " + t._string))
}
if isDirectIface(t) {
return c1 * fn(unsafe.Pointer(&a.data), h^c0)
}
eq := t.alg.equal
if eq == nil {
- panic(errorString("comparing uncomparable type " + *t._string))
+ panic(errorString("comparing uncomparable type " + t._string))
}
if isDirectIface(t) {
return eq(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data)))
t := xtab._type
eq := t.alg.equal
if eq == nil {
- panic(errorString("comparing uncomparable type " + *t._string))
+ panic(errorString("comparing uncomparable type " + t._string))
}
if isDirectIface(t) {
return eq(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data)))
// AL: byte sought
// R8: address to put result
TEXT runtime·indexbytebody(SB),NOSPLIT,$0
- MOVQ SI, DI
-
- CMPQ BX, $16
- JLT small
-
- CMPQ BX, $32
- JA avx2
-no_avx2:
- // round up to first 16-byte boundary
- TESTQ $15, SI
- JZ aligned
- MOVQ SI, CX
- ANDQ $~15, CX
- ADDQ $16, CX
-
- // search the beginning
- SUBQ SI, CX
- REPN; SCASB
- JZ success
-
-// DI is 16-byte aligned; get ready to search using SSE instructions
-aligned:
- // round down to last 16-byte boundary
- MOVQ BX, R11
- ADDQ SI, R11
- ANDQ $~15, R11
-
- // shuffle X0 around so that each byte contains c
+ // Shuffle X0 around so that each byte contains
+ // the character we're looking for.
MOVD AX, X0
PUNPCKLBW X0, X0
PUNPCKLBW X0, X0
PSHUFL $0, X0, X0
- JMP condition
+
+ CMPQ BX, $16
+ JLT small
+
+ MOVQ SI, DI
+ CMPQ BX, $32
+ JA avx2
sse:
- // move the next 16-byte chunk of the buffer into X1
- MOVO (DI), X1
- // compare bytes in X0 to X1
- PCMPEQB X0, X1
- // take the top bit of each byte in X1 and put the result in DX
+ LEAQ -16(SI)(BX*1), AX // AX = address of last 16 bytes
+ JMP sseloopentry
+
+sseloop:
+ // Move the next 16-byte chunk of the data into X1.
+ MOVOU (DI), X1
+ // Compare bytes in X0 to X1.
+ PCMPEQB X0, X1
+ // Take the top bit of each byte in X1 and put the result in DX.
PMOVMSKB X1, DX
- TESTL DX, DX
- JNZ ssesuccess
- ADDQ $16, DI
+ // Find first set bit, if any.
+ BSFL DX, DX
+ JNZ ssesuccess
+ // Advance to next block.
+ ADDQ $16, DI
+sseloopentry:
+ CMPQ DI, AX
+ JB sseloop
-condition:
- CMPQ DI, R11
- JLT sse
-
- // search the end
- MOVQ SI, CX
- ADDQ BX, CX
- SUBQ R11, CX
- // if CX == 0, the zero flag will be set and we'll end up
- // returning a false success
- JZ failure
- REPN; SCASB
- JZ success
+ // Search the last 16-byte chunk. This chunk may overlap with the
+ // chunks we've already searched, but that's ok.
+ MOVQ AX, DI
+ MOVOU (AX), X1
+ PCMPEQB X0, X1
+ PMOVMSKB X1, DX
+ BSFL DX, DX
+ JNZ ssesuccess
failure:
MOVQ $-1, (R8)
RET
+// We've found a chunk containing the byte.
+// The chunk was loaded from DI.
+// The index of the matching byte in the chunk is DX.
+// The start of the data is SI.
+ssesuccess:
+ SUBQ SI, DI // Compute offset of chunk within data.
+ ADDQ DX, DI // Add offset of byte within chunk.
+ MOVQ DI, (R8)
+ RET
+
// handle for lengths < 16
small:
- MOVQ BX, CX
- REPN; SCASB
- JZ success
- MOVQ $-1, (R8)
+ TESTQ BX, BX
+ JEQ failure
+
+ // Check if we'll load across a page boundary.
+ LEAQ 16(SI), AX
+ TESTW $0xff0, AX
+ JEQ endofpage
+
+ MOVOU (SI), X1 // Load data
+ PCMPEQB X0, X1 // Compare target byte with each byte in data.
+ PMOVMSKB X1, DX // Move result bits to integer register.
+ BSFL DX, DX // Find first set bit.
+ JZ failure // No set bit, failure.
+ CMPL DX, BX
+ JAE failure // Match is past end of data.
+ MOVQ DX, (R8)
+ RET
+
+endofpage:
+ MOVOU -16(SI)(BX*1), X1 // Load data into the high end of X1.
+ PCMPEQB X0, X1 // Compare target byte with each byte in data.
+ PMOVMSKB X1, DX // Move result bits to integer register.
+ MOVL BX, CX
+ SHLL CX, DX
+ SHRL $16, DX // Shift desired bits down to bottom of register.
+ BSFL DX, DX // Find first set bit.
+ JZ failure // No set bit, failure.
+ MOVQ DX, (R8)
RET
avx2:
CMPB runtime·support_avx2(SB), $1
- JNE no_avx2
+ JNE sse
MOVD AX, X0
LEAQ -32(SI)(BX*1), R11
VPBROADCASTB X0, Y1
VZEROUPPER
RET
-// we've found the chunk containing the byte
-// now just figure out which specific byte it is
-ssesuccess:
- // get the index of the least significant set bit
- BSFW DX, DX
- SUBQ SI, DI
- ADDQ DI, DX
- MOVQ DX, (R8)
- RET
-
-success:
- SUBQ SI, DI
- SUBL $1, DI
- MOVQ DI, (R8)
- RET
-
TEXT bytes·Equal(SB),NOSPLIT,$0-49
MOVQ a_len+8(FP), BX
MOVQ b_len+32(FP), CX
// armPublicationBarrier is a native store/store barrier for ARMv7+.
// On earlier ARM revisions, armPublicationBarrier is a no-op.
// This will not work on SMP ARMv6 machines, if any are in use.
-// To implement publiationBarrier in sys_$GOOS_arm.s using the native
+// To implement publicationBarrier in sys_$GOOS_arm.s using the native
// instructions, use:
//
// TEXT ·publicationBarrier(SB),NOSPLIT,$-4-0
--- /dev/null
+// Copyright 2016 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 runtime_test
+
+import (
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func f1(pan bool) []uintptr {
+ return f2(pan) // line 14
+}
+
+func f2(pan bool) []uintptr {
+ return f3(pan) // line 18
+}
+
+func f3(pan bool) []uintptr {
+ if pan {
+ panic("f3") // line 23
+ }
+ ret := make([]uintptr, 20)
+ return ret[:runtime.Callers(0, ret)] // line 26
+}
+
+func testCallers(t *testing.T, pcs []uintptr, pan bool) {
+ m := make(map[string]int, len(pcs))
+ frames := runtime.CallersFrames(pcs)
+ for {
+ frame, more := frames.Next()
+ if frame.Function != "" {
+ m[frame.Function] = frame.Line
+ }
+ if !more {
+ break
+ }
+ }
+
+ var seen []string
+ for k := range m {
+ seen = append(seen, k)
+ }
+ t.Logf("functions seen: %s", strings.Join(seen, " "))
+
+ var f3Line int
+ if pan {
+ f3Line = 23
+ } else {
+ f3Line = 26
+ }
+ want := []struct {
+ name string
+ line int
+ }{
+ {"f1", 14},
+ {"f2", 18},
+ {"f3", f3Line},
+ }
+ for _, w := range want {
+ if got := m["runtime_test."+w.name]; got != w.line {
+ t.Errorf("%s is line %d, want %d", w.name, got, w.line)
+ }
+ }
+}
+
+func TestCallers(t *testing.T) {
+ testCallers(t, f1(false), false)
+}
+
+func TestCallersPanic(t *testing.T) {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Fatal("did not panic")
+ }
+ pcs := make([]uintptr, 20)
+ pcs = pcs[:runtime.Callers(0, pcs)]
+ testCallers(t, pcs, true)
+ }()
+ f1(true)
+}
var x_cgo_sys_thread_create byte
var _cgo_sys_thread_create = &x_cgo_sys_thread_create
-// Notifies that the runtime has been intialized.
+// Notifies that the runtime has been initialized.
//
// We currently block at every CGO entry point (via _cgo_wait_runtime_init_done)
// to ensure that the runtime has been initialized before the CGO call is
if t0 != 0 {
mysg.releasetime = -1
}
+ // No stack splits between assigning elem and enqueuing mysg
+ // on gp.waiting where copystack can find it.
mysg.elem = ep
mysg.waitlink = nil
mysg.g = gp
if t0 != 0 {
mysg.releasetime = -1
}
+ // No stack splits between assigning elem and enqueuing mysg
+ // on gp.waiting where copystack can find it.
mysg.elem = ep
mysg.waitlink = nil
gp.waiting = mysg
}
e <- 9
}()
- time.Sleep(time.Millisecond) // make sure goroutine A gets qeueued first on c
+ time.Sleep(time.Millisecond) // make sure goroutine A gets queued first on c
// goroutine B
go func() {
package runtime_test
import (
+ "fmt"
"internal/testenv"
"os/exec"
"runtime"
t.Fatal(err)
}
- cmd := testEnv(exec.Command(exe, "CgoCheckBytes"))
+ // Try it 10 times to avoid flakiness.
+ const tries = 10
+ var tot1, tot2 time.Duration
+ for i := 0; i < tries; i++ {
+ cmd := testEnv(exec.Command(exe, "CgoCheckBytes"))
+ cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0", fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
- start := time.Now()
- cmd.Run()
- d1 := time.Since(start)
+ start := time.Now()
+ cmd.Run()
+ d1 := time.Since(start)
- cmd = testEnv(exec.Command(exe, "CgoCheckBytes"))
- cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0")
+ cmd = testEnv(exec.Command(exe, "CgoCheckBytes"))
+ cmd.Env = append(cmd.Env, fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
- start = time.Now()
- cmd.Run()
- d2 := time.Since(start)
+ start = time.Now()
+ cmd.Run()
+ d2 := time.Since(start)
- if d2*10 < d1 {
- t.Errorf("cgo check too slow: got %v, expected at most %v", d1, d2*10)
+ if d1*20 > d2 {
+ // The slow version (d2) was less than 20 times
+ // slower than the fast version (d1), so OK.
+ return
+ }
+
+ tot1 += d1
+ tot2 += d2
}
+
+ t.Errorf("cgo check too slow: got %v, expected at most %v", tot2/tries, (tot1/tries)*20)
}
func TestCgoPanicDeadlock(t *testing.T) {
func typestring(x interface{}) string {
e := efaceOf(&x)
- return *e._type._string
+ return e._type._string
}
// For calling from C.
//
// Note that since each slice entry pc[i] is a return program counter,
// looking up the file and line for pc[i] (for example, using (*Func).FileLine)
-// will return the file and line number of the instruction immediately
+// will normally return the file and line number of the instruction immediately
// following the call.
-// To look up the file and line number of the call itself, use pc[i]-1.
-// As an exception to this rule, if pc[i-1] corresponds to the function
-// runtime.sigpanic, then pc[i] is the program counter of a faulting
-// instruction and should be used without any subtraction.
+// To easily look up file/line information for the call sequence, use Frames.
func Callers(skip int, pc []uintptr) int {
// runtime.callers uses pc.array==nil as a signal
// to print a stack trace. Pick off 0-length pc here
dumpint(tagType)
dumpint(uint64(uintptr(unsafe.Pointer(t))))
dumpint(uint64(t.size))
- if t.x == nil || t.x.pkgpath == nil || t.x.name == nil {
- dumpstr(*t._string)
+ if t.x == nil || t.x.pkgpath == nil {
+ dumpstr(t._string)
} else {
pkgpath := stringStructOf(t.x.pkgpath)
- name := stringStructOf(t.x.name)
+ namestr := t.name()
+ name := stringStructOf(&namestr)
dumpint(uint64(uintptr(pkgpath.len) + 1 + uintptr(name.len)))
dwrite(pkgpath.str, uintptr(pkgpath.len))
dwritebyte('.')
func itab_callback(tab *itab) {
t := tab._type
- // Dump a map from itab* to the type of its data field.
- // We want this map so we can deduce types of interface referents.
- if t.kind&kindDirectIface == 0 {
- // indirect - data slot is a pointer to t.
- dumptype(t.ptrto)
- dumpint(tagItab)
- dumpint(uint64(uintptr(unsafe.Pointer(tab))))
- dumpint(uint64(uintptr(unsafe.Pointer(t.ptrto))))
- } else if t.kind&kindNoPointers == 0 {
- // t is pointer-like - data slot is a t.
- dumptype(t)
- dumpint(tagItab)
- dumpint(uint64(uintptr(unsafe.Pointer(tab))))
- dumpint(uint64(uintptr(unsafe.Pointer(t))))
- } else {
- // Data slot is a scalar. Dump type just for fun.
- // With pointer-only interfaces, this shouldn't happen.
- dumptype(t)
- dumpint(tagItab)
- dumpint(uint64(uintptr(unsafe.Pointer(tab))))
- dumpint(uint64(uintptr(unsafe.Pointer(t))))
- }
+ dumptype(t)
+ dumpint(tagItab)
+ dumpint(uint64(uintptr(unsafe.Pointer(tab))))
+ dumpint(uint64(uintptr(unsafe.Pointer(t))))
}
func dumpitabs() {
}
}
-var dumphdr = []byte("go1.6 heap dump\n")
+var dumphdr = []byte("go1.7 heap dump\n")
func mdump() {
// make sure we're done sweeping
if canfail {
return nil
}
- panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *inter.mhdr[0].name})
+ panic(&TypeAssertionError{"", typ._string, inter.typ._string, *inter.mhdr[0].name})
}
// compiler has provided some good hash codes for us.
if locked != 0 {
unlock(&ifaceLock)
}
- panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *iname})
+ panic(&TypeAssertionError{"", typ._string, inter.typ._string, *iname})
}
m.bad = 1
break
func panicdottype(have, want, iface *_type) {
haveString := ""
if have != nil {
- haveString = *have._string
+ haveString = have._string
}
- panic(&TypeAssertionError{*iface._string, haveString, *want._string, ""})
+ panic(&TypeAssertionError{iface._string, haveString, want._string, ""})
}
func assertI2T(t *_type, i iface, r unsafe.Pointer) {
tab := i.tab
if tab == nil {
- panic(&TypeAssertionError{"", "", *t._string, ""})
+ panic(&TypeAssertionError{"", "", t._string, ""})
}
if tab._type != t {
- panic(&TypeAssertionError{*tab.inter.typ._string, *tab._type._string, *t._string, ""})
+ panic(&TypeAssertionError{tab.inter.typ._string, tab._type._string, t._string, ""})
}
if r != nil {
if isDirectIface(t) {
func assertE2T(t *_type, e eface, r unsafe.Pointer) {
if e._type == nil {
- panic(&TypeAssertionError{"", "", *t._string, ""})
+ panic(&TypeAssertionError{"", "", t._string, ""})
}
if e._type != t {
- panic(&TypeAssertionError{"", *e._type._string, *t._string, ""})
+ panic(&TypeAssertionError{"", e._type._string, t._string, ""})
}
if r != nil {
if isDirectIface(t) {
tab := i.tab
if tab == nil {
// explicit conversions require non-nil interface value.
- panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
+ panic(&TypeAssertionError{"", "", inter.typ._string, ""})
}
r._type = tab._type
r.data = i.data
tab := i.tab
if tab == nil {
// explicit conversions require non-nil interface value.
- panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
+ panic(&TypeAssertionError{"", "", inter.typ._string, ""})
}
if tab.inter == inter {
r.tab = tab
t := e._type
if t == nil {
// explicit conversions require non-nil interface value.
- panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
+ panic(&TypeAssertionError{"", "", inter.typ._string, ""})
}
r.tab = getitab(inter, t, false)
r.data = e.data
func assertE2E(inter *interfacetype, e eface, r *eface) {
if e._type == nil {
// explicit conversions require non-nil interface value.
- panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
+ panic(&TypeAssertionError{"", "", inter.typ._string, ""})
}
*r = e
}
// The result includes in its higher bits the bits for subsequent words
// described by the same bitmap byte.
func (h heapBits) bits() uint32 {
- return uint32(*h.bitp) >> h.shift
+ // The (shift & 31) eliminates a test and conditional branch
+ // from the generated code.
+ return uint32(*h.bitp) >> (h.shift & 31)
}
// isMarked reports whether the heap bits have the marked bit set.
throw("runtime: typeBitsBulkBarrier without type")
}
if typ.size != size {
- println("runtime: typeBitsBulkBarrier with type ", *typ._string, " of size ", typ.size, " but memory size", size)
+ println("runtime: typeBitsBulkBarrier with type ", typ._string, " of size ", typ.size, " but memory size", size)
throw("runtime: invalid typeBitsBulkBarrier")
}
if typ.kind&kindGCProg != 0 {
- println("runtime: typeBitsBulkBarrier with type ", *typ._string, " with GC prog")
+ println("runtime: typeBitsBulkBarrier with type ", typ._string, " with GC prog")
throw("runtime: invalid typeBitsBulkBarrier")
}
if !writeBarrier.needed {
// TODO(rsc): Perhaps introduce a different heapBitsSpan type.
// initSpan initializes the heap bitmap for a span.
+// It clears all mark and checkmark bits.
+// If this is a span of pointer-sized objects, it initializes all
+// words to pointer (and there are no dead bits).
+// Otherwise, it initializes all words to scalar/dead.
func (h heapBits) initSpan(size, n, total uintptr) {
if total%heapBitmapScale != 0 {
throw("initSpan: unaligned length")
// heapBitsSweepSpan coordinates the sweeping of a span by reading
// and updating the corresponding heap bitmap entries.
// For each free object in the span, heapBitsSweepSpan sets the type
-// bits for the first two words (or one for single-word objects) to typeDead
+// bits for the first four words (less for smaller objects) to scalar/dead
// and then calls f(p), where p is the object's base address.
// f is expected to add the object to a free list.
// For non-free objects, heapBitsSweepSpan turns off the marked bit.
}
if nw == 0 {
// No pointers! Caller was supposed to check.
- println("runtime: invalid type ", *typ._string)
+ println("runtime: invalid type ", typ._string)
throw("heapBitsSetType: called with non-pointer type")
return
}
if doubleCheck {
end := heapBitsForAddr(x + size)
if typ.kind&kindGCProg == 0 && (hbitp != end.bitp || (w == nw+2) != (end.shift == 2)) {
- println("ended at wrong bitmap byte for", *typ._string, "x", dataSize/typ.size)
+ println("ended at wrong bitmap byte for", typ._string, "x", dataSize/typ.size)
print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n")
print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n")
h0 := heapBitsForAddr(x)
}
}
if have != want {
- println("mismatch writing bits for", *typ._string, "x", dataSize/typ.size)
+ println("mismatch writing bits for", typ._string, "x", dataSize/typ.size)
print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n")
print("kindGCProg=", typ.kind&kindGCProg != 0, "\n")
print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n")
s.needzero = 1
s.freelist = 0
unlock(&c.lock)
- heapBitsForSpan(s.base()).initSpan(s.layout())
mheap_.freeSpan(s, 0)
return true
}
ADDQ $128, DI
CMPQ BX, $128
JAE loop_avx2_huge
- // In the desciption of MOVNTDQ in [1]
+ // In the description of MOVNTDQ in [1]
// "... fencing operation implemented with the SFENCE or MFENCE instruction
// should be used in conjunction with MOVNTDQ instructions..."
// [1] 64-ia-32-architectures-software-developer-manual-325462.pdf
BC 12, 9, backward // I think you should be able to write this as "BGT CR2, backward"
// Copying forward proceeds by copying R6 words then copying R7 bytes.
- // R3 and R4 are advanced as we copy. Becuase PPC64 lacks post-increment
+ // R3 and R4 are advanced as we copy. Because PPC64 lacks post-increment
// load/store, R3 and R4 point before the bytes that are to be copied.
BC 12, 6, noforwardlarge // "BEQ CR1, noforwardlarge"
throw("runtime.SetFinalizer: first argument is nil")
}
if etyp.kind&kindMask != kindPtr {
- throw("runtime.SetFinalizer: first argument is " + *etyp._string + ", not pointer")
+ throw("runtime.SetFinalizer: first argument is " + etyp._string + ", not pointer")
}
ot := (*ptrtype)(unsafe.Pointer(etyp))
if ot.elem == nil {
}
if ftyp.kind&kindMask != kindFunc {
- throw("runtime.SetFinalizer: second argument is " + *ftyp._string + ", not a function")
+ throw("runtime.SetFinalizer: second argument is " + ftyp._string + ", not a function")
}
ft := (*functype)(unsafe.Pointer(ftyp))
if ft.dotdotdot || len(ft.in) != 1 {
- throw("runtime.SetFinalizer: cannot pass " + *etyp._string + " to finalizer " + *ftyp._string)
+ throw("runtime.SetFinalizer: cannot pass " + etyp._string + " to finalizer " + ftyp._string)
}
fint := ft.in[0]
switch {
// ok - same type
goto okarg
case fint.kind&kindMask == kindPtr:
- if (fint.x == nil || fint.x.name == nil || etyp.x == nil || etyp.x.name == nil) && (*ptrtype)(unsafe.Pointer(fint)).elem == ot.elem {
+ if (fint.x == nil || etyp.x == nil) && (*ptrtype)(unsafe.Pointer(fint)).elem == ot.elem {
// ok - not same type, but both pointers,
// one or the other is unnamed, and same element type, so assignable.
goto okarg
goto okarg
}
}
- throw("runtime.SetFinalizer: cannot pass " + *etyp._string + " to finalizer " + *ftyp._string)
+ throw("runtime.SetFinalizer: cannot pass " + etyp._string + " to finalizer " + ftyp._string)
okarg:
// compute size needed for return parameters
nret := uintptr(0)
gchelperstart()
- var gcw gcWork
- gcDrain(&gcw, gcDrainBlock)
+ gcw := &getg().m.p.ptr().gcw
+ gcDrain(gcw, gcDrainBlock)
gcw.dispose()
gcMarkRootCheck()
// Parallel mark over GC roots and heap
if gcphase == _GCmarktermination {
- var gcw gcWork
- gcDrain(&gcw, gcDrainBlock) // blocks in getfull
+ gcw := &_g_.m.p.ptr().gcw
+ gcDrain(gcw, gcDrainBlock) // blocks in getfull
gcw.dispose()
}
// Preemption must be disabled (because this uses a gcWork).
//
//go:nowritebarrier
-func markroot(i uint32) {
- // TODO: Consider using getg().m.p.ptr().gcw.
- var gcw gcWork
-
+func markroot(gcw *gcWork, i uint32) {
baseData := uint32(fixedRootCount)
baseBSS := baseData + uint32(work.nDataRoots)
baseSpans := baseBSS + uint32(work.nBSSRoots)
switch {
case baseData <= i && i < baseBSS:
for datap := &firstmoduledata; datap != nil; datap = datap.next {
- markrootBlock(datap.data, datap.edata-datap.data, datap.gcdatamask.bytedata, &gcw, int(i-baseData))
+ markrootBlock(datap.data, datap.edata-datap.data, datap.gcdatamask.bytedata, gcw, int(i-baseData))
}
case baseBSS <= i && i < baseSpans:
for datap := &firstmoduledata; datap != nil; datap = datap.next {
- markrootBlock(datap.bss, datap.ebss-datap.bss, datap.gcbssmask.bytedata, &gcw, int(i-baseBSS))
+ markrootBlock(datap.bss, datap.ebss-datap.bss, datap.gcbssmask.bytedata, gcw, int(i-baseBSS))
}
case i == fixedRootFinalizers:
for fb := allfin; fb != nil; fb = fb.alllink {
- scanblock(uintptr(unsafe.Pointer(&fb.fin[0])), uintptr(fb.cnt)*unsafe.Sizeof(fb.fin[0]), &finptrmask[0], &gcw)
+ scanblock(uintptr(unsafe.Pointer(&fb.fin[0])), uintptr(fb.cnt)*unsafe.Sizeof(fb.fin[0]), &finptrmask[0], gcw)
}
case i == fixedRootFlushCaches:
case baseSpans <= i && i < baseStacks:
// mark MSpan.specials
- markrootSpans(&gcw, int(i-baseSpans))
+ markrootSpans(gcw, int(i-baseSpans))
default:
// the rest is scanning goroutine stacks
}
})
}
-
- gcw.dispose()
}
// markrootBlock scans the shard'th shard of the block of memory [b0,
if job >= work.markrootJobs {
break
}
- // TODO: Pass in gcw.
- markroot(job)
+ markroot(gcw, job)
}
}
if preserve {
throw("can't preserve large span")
}
- heapBitsForSpan(p).initSpan(s.layout())
s.needzero = 1
// Free the span after heapBitsSweepSpan
)
const (
- _Debugwbufs = false // if true check wbufs consistency
- _WorkbufSize = 2048 // in bytes; larger values result in less contention
+ _WorkbufSize = 2048 // in bytes; larger values result in less contention
)
// Garbage collector work pool abstraction.
//
// A gcWork can be used on the stack as follows:
//
-// var gcw gcWork
-// disable preemption
-// .. call gcw.put() to produce and gcw.get() to consume ..
-// gcw.dispose()
-// enable preemption
-//
-// Or from the per-P gcWork cache:
-//
// (preemption must be disabled)
// gcw := &getg().m.p.ptr().gcw
// .. call gcw.put() to produce and gcw.get() to consume ..
}
func (w *gcWork) init() {
- w.wbuf1 = wbufptrOf(getempty(101))
- wbuf2 := trygetfull(102)
+ w.wbuf1 = wbufptrOf(getempty())
+ wbuf2 := trygetfull()
if wbuf2 == nil {
- wbuf2 = getempty(103)
+ wbuf2 = getempty()
}
w.wbuf2 = wbufptrOf(wbuf2)
}
// put enqueues a pointer for the garbage collector to trace.
// obj must point to the beginning of a heap object.
//go:nowritebarrier
-func (ww *gcWork) put(obj uintptr) {
- w := (*gcWork)(noescape(unsafe.Pointer(ww))) // TODO: remove when escape analysis is fixed
-
+func (w *gcWork) put(obj uintptr) {
wbuf := w.wbuf1.ptr()
if wbuf == nil {
w.init()
w.wbuf1, w.wbuf2 = w.wbuf2, w.wbuf1
wbuf = w.wbuf1.ptr()
if wbuf.nobj == len(wbuf.obj) {
- putfull(wbuf, 132)
- wbuf = getempty(133)
+ putfull(wbuf)
+ wbuf = getempty()
w.wbuf1 = wbufptrOf(wbuf)
}
}
// queue, tryGet returns 0. Note that there may still be pointers in
// other gcWork instances or other caches.
//go:nowritebarrier
-func (ww *gcWork) tryGet() uintptr {
- w := (*gcWork)(noescape(unsafe.Pointer(ww))) // TODO: remove when escape analysis is fixed
-
+func (w *gcWork) tryGet() uintptr {
wbuf := w.wbuf1.ptr()
if wbuf == nil {
w.init()
wbuf = w.wbuf1.ptr()
if wbuf.nobj == 0 {
owbuf := wbuf
- wbuf = trygetfull(167)
+ wbuf = trygetfull()
if wbuf == nil {
return 0
}
- putempty(owbuf, 166)
+ putempty(owbuf)
w.wbuf1 = wbufptrOf(wbuf)
}
}
// if necessary to ensure all pointers from all queues and caches have
// been retrieved. get returns 0 if there are no pointers remaining.
//go:nowritebarrier
-func (ww *gcWork) get() uintptr {
- w := (*gcWork)(noescape(unsafe.Pointer(ww))) // TODO: remove when escape analysis is fixed
-
+func (w *gcWork) get() uintptr {
wbuf := w.wbuf1.ptr()
if wbuf == nil {
w.init()
wbuf = w.wbuf1.ptr()
if wbuf.nobj == 0 {
owbuf := wbuf
- wbuf = getfull(185)
+ wbuf = getfull()
if wbuf == nil {
return 0
}
- putempty(owbuf, 184)
+ putempty(owbuf)
w.wbuf1 = wbufptrOf(wbuf)
}
}
func (w *gcWork) dispose() {
if wbuf := w.wbuf1.ptr(); wbuf != nil {
if wbuf.nobj == 0 {
- putempty(wbuf, 212)
+ putempty(wbuf)
} else {
- putfull(wbuf, 214)
+ putfull(wbuf)
}
w.wbuf1 = 0
wbuf = w.wbuf2.ptr()
if wbuf.nobj == 0 {
- putempty(wbuf, 218)
+ putempty(wbuf)
} else {
- putfull(wbuf, 220)
+ putfull(wbuf)
}
w.wbuf2 = 0
}
return
}
if wbuf := w.wbuf2.ptr(); wbuf.nobj != 0 {
- putfull(wbuf, 246)
- w.wbuf2 = wbufptrOf(getempty(247))
+ putfull(wbuf)
+ w.wbuf2 = wbufptrOf(getempty())
} else if wbuf := w.wbuf1.ptr(); wbuf.nobj > 4 {
w.wbuf1 = wbufptrOf(handoff(wbuf))
}
// avoid contending on the global work buffer lists.
type workbufhdr struct {
- node lfnode // must be first
- nobj int
- inuse bool // This workbuf is in use by some gorotuine and is not on the work.empty/full queues.
- log [4]int // line numbers forming a history of ownership changes to workbuf
+ node lfnode // must be first
+ nobj int
}
type workbuf struct {
// workbufs.
// If the GC asks for some work these are the only routines that
// make wbufs available to the GC.
-// Each of the gets and puts also take an distinct integer that is used
-// to record a brief history of changes to ownership of the workbuf.
-// The convention is to use a unique line number but any encoding
-// is permissible. For example if you want to pass in 2 bits of information
-// you could simple add lineno1*100000+lineno2.
-
-// logget records the past few values of entry to aid in debugging.
-// logget checks the buffer b is not currently in use.
-func (b *workbuf) logget(entry int) {
- if !_Debugwbufs {
- return
- }
- if b.inuse {
- println("runtime: logget fails log entry=", entry,
- "b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
- "b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
- throw("logget: get not legal")
- }
- b.inuse = true
- copy(b.log[1:], b.log[:])
- b.log[0] = entry
-}
-
-// logput records the past few values of entry to aid in debugging.
-// logput checks the buffer b is currently in use.
-func (b *workbuf) logput(entry int) {
- if !_Debugwbufs {
- return
- }
- if !b.inuse {
- println("runtime: logput fails log entry=", entry,
- "b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
- "b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
- throw("logput: put not legal")
- }
- b.inuse = false
- copy(b.log[1:], b.log[:])
- b.log[0] = entry
-}
func (b *workbuf) checknonempty() {
if b.nobj == 0 {
- println("runtime: nonempty check fails",
- "b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
- "b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
throw("workbuf is empty")
}
}
func (b *workbuf) checkempty() {
if b.nobj != 0 {
- println("runtime: empty check fails",
- "b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
- "b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
throw("workbuf is not empty")
}
}
// getempty pops an empty work buffer off the work.empty list,
// allocating new buffers if none are available.
-// entry is used to record a brief history of ownership.
//go:nowritebarrier
-func getempty(entry int) *workbuf {
+func getempty() *workbuf {
var b *workbuf
if work.empty != 0 {
b = (*workbuf)(lfstackpop(&work.empty))
if b == nil {
b = (*workbuf)(persistentalloc(unsafe.Sizeof(*b), sys.CacheLineSize, &memstats.gc_sys))
}
- b.logget(entry)
return b
}
// putempty puts a workbuf onto the work.empty list.
// Upon entry this go routine owns b. The lfstackpush relinquishes ownership.
//go:nowritebarrier
-func putempty(b *workbuf, entry int) {
+func putempty(b *workbuf) {
b.checkempty()
- b.logput(entry)
lfstackpush(&work.empty, &b.node)
}
// putfull accepts partially full buffers so the GC can avoid competing
// with the mutators for ownership of partially full buffers.
//go:nowritebarrier
-func putfull(b *workbuf, entry int) {
+func putfull(b *workbuf) {
b.checknonempty()
- b.logput(entry)
lfstackpush(&work.full, &b.node)
// We just made more work available. Let the GC controller
// trygetfull tries to get a full or partially empty workbuffer.
// If one is not immediately available return nil
//go:nowritebarrier
-func trygetfull(entry int) *workbuf {
+func trygetfull() *workbuf {
b := (*workbuf)(lfstackpop(&work.full))
if b != nil {
- b.logget(entry)
b.checknonempty()
return b
}
// This is in fact the termination condition for the STW mark
// phase.
//go:nowritebarrier
-func getfull(entry int) *workbuf {
+func getfull() *workbuf {
b := (*workbuf)(lfstackpop(&work.full))
if b != nil {
- b.logget(entry)
b.checknonempty()
return b
}
}
b = (*workbuf)(lfstackpop(&work.full))
if b != nil {
- b.logget(entry)
b.checknonempty()
return b
}
//go:nowritebarrier
func handoff(b *workbuf) *workbuf {
// Make new buffer with half of b's pointers.
- b1 := getempty(915)
+ b1 := getempty()
n := b.nobj / 2
b.nobj -= n
b1.nobj = n
_g_.m.gcstats.nhandoffcnt += uint64(n)
// Put b on full list - let first half of b get stolen.
- putfull(b, 942)
+ putfull(b)
return b1
}
if typ == nil {
print("tracealloc(", p, ", ", hex(size), ")\n")
} else {
- print("tracealloc(", p, ", ", hex(size), ", ", *typ._string, ")\n")
+ print("tracealloc(", p, ", ", hex(size), ", ", typ._string, ")\n")
}
if gp.m.curg == nil || gp == gp.m.curg {
goroutineheader(gp)
//
// The open and arming mechanisms are serialized using the lock
// inside PollDesc. This is required because the netpoll loop runs
-// asynchonously in respect to other Go code and by the time we get
+// asynchronously in respect to other Go code and by the time we get
// to call port_associate to update the association in the loop, the
// file descriptor might have been closed and reopened already. The
// lock allows runtime·netpollupdate to be called synchronously from
lock(&pd.lock)
// We don't register for any specific type of events yet, that's
// netpollarm's job. We merely ensure we call port_associate before
- // asynchonous connect/accept completes, so when we actually want
+ // asynchronous connect/accept completes, so when we actually want
// to do any I/O, the call to port_associate (from netpollarm,
// with the interested event set) will unblock port_getn right away
// because of the I/O readiness notification.
_AT_SYSINFO = 32
)
-var _vdso uint32
-
func sysargs(argc int32, argv **byte) {
// skip over argv, envv to get to auxv
n := argc + 1
for i := 0; auxv[i] != _AT_NULL; i += 2 {
switch auxv[i] {
- case _AT_SYSINFO:
- _vdso = auxv[i+1]
-
case _AT_RANDOM:
startupRandomData = (*[16]byte)(unsafe.Pointer(uintptr(auxv[i+1])))[:]
}
// for a single stack trace.
func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) {
show := allFrames
- wasPanic := false
- for i, pc := range stk {
- f := runtime.FuncForPC(pc)
- if f == nil {
+ frames := runtime.CallersFrames(stk)
+ for {
+ frame, more := frames.Next()
+ name := frame.Function
+ if name == "" {
show = true
- fmt.Fprintf(w, "#\t%#x\n", pc)
- wasPanic = false
+ fmt.Fprintf(w, "#\t%#x\n", frame.PC)
} else {
- tracepc := pc
- // Back up to call instruction.
- if i > 0 && pc > f.Entry() && !wasPanic {
- if runtime.GOARCH == "386" || runtime.GOARCH == "amd64" {
- tracepc--
- } else {
- tracepc -= 4 // arm, etc
- }
- }
- file, line := f.FileLine(tracepc)
- name := f.Name()
// Hide runtime.goexit and any runtime functions at the beginning.
// This is useful mainly for allocation traces.
- wasPanic = name == "runtime.gopanic"
if name == "runtime.goexit" || !show && strings.HasPrefix(name, "runtime.") {
continue
}
show = true
- fmt.Fprintf(w, "#\t%#x\t%s+%#x\t%s:%d\n", pc, name, pc-f.Entry(), file, line)
+ fmt.Fprintf(w, "#\t%#x\t%s+%#x\t%s:%d\n", frame.PC, name, frame.PC-frame.Entry, frame.File, frame.Line)
+ }
+ if !more {
+ break
}
}
if !show {
if newval == oldval&^_Gscan {
success = atomic.Cas(&gp.atomicstatus, oldval, newval)
}
- case _Gscanenqueue:
- if newval == _Gwaiting {
- success = atomic.Cas(&gp.atomicstatus, oldval, newval)
- }
}
if !success {
print("runtime: casfrom_Gscanstatus failed gp=", gp, ", oldval=", hex(oldval), ", newval=", hex(newval), "\n")
func castogscanstatus(gp *g, oldval, newval uint32) bool {
switch oldval {
case _Grunnable,
+ _Grunning,
_Gwaiting,
_Gsyscall:
if newval == oldval|_Gscan {
return atomic.Cas(&gp.atomicstatus, oldval, newval)
}
- case _Grunning:
- if newval == _Gscanrunning || newval == _Gscanenqueue {
- return atomic.Cas(&gp.atomicstatus, oldval, newval)
- }
}
print("runtime: castogscanstatus oldval=", hex(oldval), " newval=", hex(newval), "\n")
throw("castogscanstatus")
_Gscanwaiting,
_Gscansyscall:
casfrom_Gscanstatus(gp, s, s&^_Gscan)
-
- // Scan is now completed.
- // Goroutine now needs to be made runnable.
- // We put it on the global run queue; ready blocks on the global scheduler lock.
- case _Gscanenqueue:
- casfrom_Gscanstatus(gp, _Gscanenqueue, _Gwaiting)
- if gp != getg().m.curg {
- throw("processing Gscanenqueue on wrong m")
- }
- dropg()
- ready(gp, 0)
}
}
func dropg() {
_g_ := getg()
- if _g_.m.lockedg == nil {
- _g_.m.curg.m = nil
- _g_.m.curg = nil
- }
+ _g_.m.curg.m = nil
+ _g_.m.curg = nil
}
func parkunlock_c(gp *g, lock unsafe.Pointer) bool {
if runqputslow(_p_, gp, h, t) {
return
}
- // the queue is not full, now the put above must suceed
+ // the queue is not full, now the put above must succeed
goto retry
}
PASS
Found 1 data race\(s\)
FAIL`},
+
+ {"slicebytetostring_pc", "run", "atexit_sleep_ms=0", `
+package main
+func main() {
+ done := make(chan string)
+ data := make([]byte, 10)
+ go func() {
+ done <- string(data)
+ }()
+ data[0] = 1
+ <-done
+}
+`, `
+ runtime\.slicebytetostring\(\)
+ .*/runtime/string\.go:.*
+ main\.main\.func1\(\)
+ .*/main.go:7`},
}
TEXT main(SB),NOSPLIT,$0
JMP runtime·rt0_go(SB)
-
-TEXT _fallback_vdso(SB),NOSPLIT,$0
- INT $0x80
- RET
-
-DATA runtime·_vdso(SB)/4, $_fallback_vdso(SB)
-GLOBL runtime·_vdso(SB), NOPTR, $4
-
argv **byte
)
-// nosplit for use in linux/386 startup linux_setup_vdso
+// nosplit for use in linux startup sysargs
//go:nosplit
func argv_index(argv **byte, i int32) *byte {
return *(**byte)(add(unsafe.Pointer(argv), uintptr(i)*sys.PtrSize))
_Gwaiting // 4
_Gmoribund_unused // 5 currently unused, but hardcoded in gdb scripts
_Gdead // 6
- _Genqueue // 7 Only the Gscanenqueue is used.
+ _Genqueue_unused // 7 currently unused
_Gcopystack // 8 in this state when newstack is moving the stack
// the following encode that the GC is scanning the stack and what to do when it is done
_Gscan = 0x1000 // atomicstatus&~Gscan = the non-scan state,
_Gscanwaiting = _Gscan + _Gwaiting // 0x1004 When scanning completes make it Gwaiting
// _Gscanmoribund_unused, // not possible
// _Gscandead, // not possible
- _Gscanenqueue = _Gscan + _Genqueue // When scanning completes make it Grunnable and put on runqueue
+ // _Gscanenqueue_unused // not possible
)
const (
// of the larger addresses must themselves be invalid addresses.
// We might get unlucky and the OS might have mapped one of these
// addresses, but probably not: they're all in the first page, very high
-// adderesses that normally an OS would reserve for itself, or malformed
+// addresses that normally an OS would reserve for itself, or malformed
// addresses. Even so, we might have to remove one or two on different
// systems. We will see.
sg.g = gp
// Note: selectdone is adjusted for stack copies in stack1.go:adjustsudogs
sg.selectdone = (*uint32)(noescape(unsafe.Pointer(&done)))
+ // No stack splits between assigning elem and enqueuing
+ // sg on gp.waiting where copystack can find it.
sg.elem = cas.elem
sg.releasetime = 0
if t0 != 0 {
level, _, docrash := gotraceback()
if level > 0 {
goroutineheader(gp)
-
- // On Linux/386, all system calls go through the vdso kernel_vsyscall routine.
- // Normally we don't see those PCs, but during signals we can.
- // If we see a PC in the vsyscall area (it moves around, but near the top of memory),
- // assume we're blocked in the vsyscall routine, which has saved
- // three words on the stack after the initial call saved the caller PC.
- // Pop all four words off SP and use the saved PC.
- // The check of the stack bounds here should suffice to avoid a fault
- // during the actual PC pop.
- // If we do load a bogus PC, not much harm done: we weren't going
- // to get a decent traceback anyway.
- // TODO(rsc): Make this more precise: we should do more checks on the PC,
- // and we should find out whether different versions of the vdso page
- // use different prologues that store different amounts on the stack.
- pc := uintptr(c.eip())
- sp := uintptr(c.esp())
- if GOOS == "linux" && pc >= 0xf4000000 && gp.stack.lo <= sp && sp+16 <= gp.stack.hi {
- // Assume in vsyscall page.
- sp += 16
- pc = *(*uintptr)(unsafe.Pointer(sp - 4))
- print("runtime: unwind vdso kernel_vsyscall: pc=", hex(pc), " sp=", hex(sp), "\n")
- }
-
- tracebacktrap(pc, sp, 0, gp)
+ tracebacktrap(uintptr(c.eip()), uintptr(c.esp()), 0, gp)
if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
// tracebackothers on original m skipped this one; trace it now.
goroutineheader(_g_.m.curg)
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Software floating point interpretaton of ARM 7500 FP instructions.
+// Software floating point interpretation of ARM 7500 FP instructions.
// The interpretation is not bit compatible with the 7500.
// It uses true little-endian doubles, while the 7500 used mixed-endian.
if raceenabled && l > 0 {
racereadrangepc(unsafe.Pointer(&b[0]),
uintptr(l),
- getcallerpc(unsafe.Pointer(&b)),
+ getcallerpc(unsafe.Pointer(&buf)),
funcPC(slicebytetostring))
}
if msanenabled && l > 0 {
if raceenabled && len(a) > 0 {
racereadrangepc(unsafe.Pointer(&a[0]),
uintptr(len(a))*unsafe.Sizeof(a[0]),
- getcallerpc(unsafe.Pointer(&a)),
+ getcallerpc(unsafe.Pointer(&buf)),
funcPC(slicerunetostring))
}
if msanenabled && len(a) > 0 {
"unsafe"
)
+// Frames may be used to get function/file/line information for a
+// slice of PC values returned by Callers.
+type Frames struct {
+ callers []uintptr
+
+ // If previous caller in iteration was a panic, then
+ // ci.callers[0] is the address of the faulting instruction
+ // instead of the return address of the call.
+ wasPanic bool
+}
+
+// Frame is the information returned by Frames for each call frame.
+type Frame struct {
+ // Program counter for this frame; multiple frames may have
+ // the same PC value.
+ PC uintptr
+
+ // Func for this frame; may be nil for non-Go code or fully
+ // inlined functions.
+ Func *Func
+
+ // Function name, file name, and line number for this call frame.
+ // May be the empty string or zero if not known.
+ // If Func is not nil then Function == Func.Name().
+ Function string
+ File string
+ Line int
+
+ // Entry point for the function; may be zero if not known.
+ // If Func is not nil then Entry == Func.Entry().
+ Entry uintptr
+}
+
+// CallersFrames takes a slice of PC values returned by Callers and
+// prepares to return function/file/line information.
+// Do not change the slice until you are done with the Frames.
+func CallersFrames(callers []uintptr) *Frames {
+ return &Frames{callers, false}
+}
+
+// Next returns frame information for the next caller.
+// If more is false, there are no more callers (the Frame value is valid).
+func (ci *Frames) Next() (frame Frame, more bool) {
+ if len(ci.callers) == 0 {
+ ci.wasPanic = false
+ return Frame{}, false
+ }
+ pc := ci.callers[0]
+ ci.callers = ci.callers[1:]
+ more = len(ci.callers) > 0
+ f := FuncForPC(pc)
+ if f == nil {
+ ci.wasPanic = false
+ return Frame{}, more
+ }
+
+ entry := f.Entry()
+ xpc := pc
+ if xpc > entry && !ci.wasPanic {
+ xpc--
+ }
+ file, line := f.FileLine(xpc)
+
+ function := f.Name()
+ ci.wasPanic = entry == sigpanicPC
+
+ frame = Frame{
+ PC: xpc,
+ Func: f,
+ Function: function,
+ File: file,
+ Line: line,
+ Entry: entry,
+ }
+
+ return frame, more
+}
+
// NOTE: Func does not expose the actual unexported fields, because we return *Func
// values to users, and we want to keep them from being able to overwrite the data
// with (say) *f = Func{}.
// Most linux systems use glibc's dynamic linker, which puts the
// __kernel_vsyscall vdso helper at 0x10(GS) for easy access from position
-// independent code and setldt in this file does the same in the statically
-// linked case. Android, however, uses bionic's dynamic linker, which does not
-// save the helper anywhere, and so the only way to invoke a syscall from
-// position independent code is boring old int $0x80 (which is also what
-// bionic's syscall wrappers use).
-#ifdef GOOS_android
+// independent code and setldt in runtime does the same in the statically
+// linked case. However, systems that use alternative libc such as Android's
+// bionic and musl, do not save the helper anywhere, and so the only way to
+// invoke a syscall from position independent code is boring old int $0x80
+// (which is also what syscall wrappers in bionic/musl use).
+//
+// The benchmarks also showed that using int $0x80 is as fast as calling
+// *%gs:0x10 except on AMD Opteron. See https://golang.org/cl/19833
+// for the benchmark program and raw data.
+//#define INVOKE_SYSCALL CALL 0x10(GS) // non-portable
#define INVOKE_SYSCALL INT $0x80
-#else
-#define INVOKE_SYSCALL CALL 0x10(GS)
-#endif
TEXT runtime·exit(SB),NOSPLIT,$0
MOVL $252, AX // syscall number
*/
ADDL $0x4, DX // address
MOVL DX, 0(DX)
- // We copy the glibc dynamic linker behaviour of storing the
- // __kernel_vsyscall entry point at 0x10(GS) so that it can be invoked
- // by "CALL 0x10(GS)" in all situations, not only those where the
- // binary is actually dynamically linked.
- MOVL runtime·_vdso(SB), AX
- MOVL AX, 0x10(DX)
#endif
// set up user_desc
// pipe(3c) wrapper that returns fds in AX, DX.
// NOT USING GO CALLING CONVENTION.
TEXT runtime·pipe1(SB),NOSPLIT,$0
- SUBQ $16, SP // 8 bytes will do, but stack has to be 16-byte alligned
+ SUBQ $16, SP // 8 bytes will do, but stack has to be 16-byte aligned
MOVQ SP, DI
LEAQ libc_pipe(SB), AX
CALL AX
import "C"
import (
"fmt"
+ "os"
"runtime"
+ "strconv"
"time"
"unsafe"
)
}
func CgoCheckBytes() {
- b := make([]byte, 1e6)
- for i := 0; i < 1e3; i++ {
+ try, _ := strconv.Atoi(os.Getenv("GO_CGOCHECKBYTES_TRY"))
+ if try <= 0 {
+ try = 1
+ }
+ b := make([]byte, 1e6*try)
+ start := time.Now()
+ for i := 0; i < 1e3*try; i++ {
C.foo2(unsafe.Pointer(&b[0]))
+ if time.Since(start) > time.Second {
+ break
+ }
}
}
_Gsyscall: "syscall",
_Gwaiting: "waiting",
_Gdead: "dead",
- _Genqueue: "enqueue",
_Gcopystack: "copystack",
}
// If the KindGCProg bit is set in kind, gcdata is a GC program.
// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
gcdata *byte
- _string *string
+ _string string
x *uncommontype
- ptrto *_type
+}
+
+func hasPrefix(s, prefix string) bool {
+ return len(s) >= len(prefix) && s[:len(prefix)] == prefix
+}
+
+func (t *_type) name() string {
+ if hasPrefix(t._string, "map[") {
+ return ""
+ }
+ if hasPrefix(t._string, "struct {") {
+ return ""
+ }
+ if hasPrefix(t._string, "chan ") {
+ return ""
+ }
+ if hasPrefix(t._string, "func(") {
+ return ""
+ }
+ if t._string[0] == '[' || t._string[0] == '*' {
+ return ""
+ }
+ i := len(t._string) - 1
+ for i >= 0 {
+ if t._string[i] == '.' {
+ break
+ }
+ i--
+ }
+ return t._string[i+1:]
}
type method struct {
}
type uncommontype struct {
- name *string
pkgpath *string
mhdr []method
}
pivot := lo
a, c := lo+1, hi-1
- for ; a != c && data.Less(a, pivot); a++ {
+ for ; a < c && data.Less(a, pivot); a++ {
}
b := a
for {
- for ; b != c && !data.Less(pivot, b); b++ { // data[b] <= pivot
+ for ; b < c && !data.Less(pivot, b); b++ { // data[b] <= pivot
}
- for ; b != c && data.Less(pivot, c-1); c-- { // data[c-1] > pivot
+ for ; b < c && data.Less(pivot, c-1); c-- { // data[c-1] > pivot
}
- if b == c {
+ if b >= c {
break
}
// data[b] > pivot; data[c-1] <= pivot
// data[a <= i < b] unexamined
// data[b <= i < c] = pivot
for {
- for ; a != b && !data.Less(b-1, pivot); b-- { // data[b] == pivot
+ for ; a < b && !data.Less(b-1, pivot); b-- { // data[b] == pivot
}
- for ; a != b && data.Less(a, pivot); a++ { // data[a] < pivot
+ for ; a < b && data.Less(a, pivot); a++ { // data[a] < pivot
}
- if a == b {
+ if a >= b {
break
}
// data[a] == pivot; data[b-1] < pivot
}
}
+type nonDeterministicTestingData struct {
+ r *rand.Rand
+}
+
+func (t *nonDeterministicTestingData) Len() int {
+ return 500
+}
+func (t *nonDeterministicTestingData) Less(i, j int) bool {
+ if i < 0 || j < 0 || i >= t.Len() || j >= t.Len() {
+ panic("nondeterministic comparison out of bounds")
+ }
+ return t.r.Float32() < 0.5
+}
+func (t *nonDeterministicTestingData) Swap(i, j int) {
+ if i < 0 || j < 0 || i >= t.Len() || j >= t.Len() {
+ panic("nondeterministic comparison out of bounds")
+ }
+}
+
+func TestNonDeterministicComparison(t *testing.T) {
+ // Ensure that sort.Sort does not panic when Less returns inconsistent results.
+ // See https://golang.org/issue/14377.
+ defer func() {
+ if r := recover(); r != nil {
+ t.Error(r)
+ }
+ }()
+
+ td := &nonDeterministicTestingData{
+ r: rand.New(rand.NewSource(0)),
+ }
+
+ for i := 0; i < 10; i++ {
+ Sort(td)
+ }
+}
+
func BenchmarkSortString1K(b *testing.B) {
b.StopTimer()
for i := 0; i < b.N; i++ {
data.initB()
Stable(data)
if !IsSorted(data) {
- t.Errorf("Stable shuffeled sorted %d ints (order)", n)
+ t.Errorf("Stable shuffled sorted %d ints (order)", n)
}
if !data.inOrder() {
- t.Errorf("Stable shuffeled sorted %d ints (stability)", n)
+ t.Errorf("Stable shuffled sorted %d ints (stability)", n)
}
// sorted reversed
package strconv
-import (
- "unicode/utf8"
-)
+import "unicode/utf8"
const lowerhex = "0123456789abcdef"
func quoteWith(s string, quote byte, ASCIIonly, graphicOnly bool) string {
- var runeTmp [utf8.UTFMax]byte
- buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations.
+ return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote, ASCIIonly, graphicOnly))
+}
+
+func quoteRuneWith(r rune, quote byte, ASCIIonly, graphicOnly bool) string {
+ return string(appendQuotedRuneWith(nil, r, quote, ASCIIonly, graphicOnly))
+}
+
+func appendQuotedWith(buf []byte, s string, quote byte, ASCIIonly, graphicOnly bool) []byte {
buf = append(buf, quote)
for width := 0; len(s) > 0; s = s[width:] {
r := rune(s[0])
buf = append(buf, lowerhex[s[0]&0xF])
continue
}
- if r == rune(quote) || r == '\\' { // always backslashed
- buf = append(buf, '\\')
+ buf = appendEscapedRune(buf, r, width, quote, ASCIIonly, graphicOnly)
+ }
+ buf = append(buf, quote)
+ return buf
+}
+
+func appendQuotedRuneWith(buf []byte, r rune, quote byte, ASCIIonly, graphicOnly bool) []byte {
+ buf = append(buf, quote)
+ if !utf8.ValidRune(r) {
+ r = utf8.RuneError
+ }
+ buf = appendEscapedRune(buf, r, utf8.RuneLen(r), quote, ASCIIonly, graphicOnly)
+ buf = append(buf, quote)
+ return buf
+}
+
+func appendEscapedRune(buf []byte, r rune, width int, quote byte, ASCIIonly, graphicOnly bool) []byte {
+ var runeTmp [utf8.UTFMax]byte
+ if r == rune(quote) || r == '\\' { // always backslashed
+ buf = append(buf, '\\')
+ buf = append(buf, byte(r))
+ return buf
+ }
+ if ASCIIonly {
+ if r < utf8.RuneSelf && IsPrint(r) {
buf = append(buf, byte(r))
- continue
+ return buf
}
- if ASCIIonly {
- if r < utf8.RuneSelf && IsPrint(r) {
- buf = append(buf, byte(r))
- continue
+ } else if IsPrint(r) || graphicOnly && isInGraphicList(r) {
+ n := utf8.EncodeRune(runeTmp[:], r)
+ buf = append(buf, runeTmp[:n]...)
+ return buf
+ }
+ switch r {
+ case '\a':
+ buf = append(buf, `\a`...)
+ case '\b':
+ buf = append(buf, `\b`...)
+ case '\f':
+ buf = append(buf, `\f`...)
+ case '\n':
+ buf = append(buf, `\n`...)
+ case '\r':
+ buf = append(buf, `\r`...)
+ case '\t':
+ buf = append(buf, `\t`...)
+ case '\v':
+ buf = append(buf, `\v`...)
+ default:
+ switch {
+ case r < ' ':
+ buf = append(buf, `\x`...)
+ buf = append(buf, lowerhex[byte(r)>>4])
+ buf = append(buf, lowerhex[byte(r)&0xF])
+ case r > utf8.MaxRune:
+ r = 0xFFFD
+ fallthrough
+ case r < 0x10000:
+ buf = append(buf, `\u`...)
+ for s := 12; s >= 0; s -= 4 {
+ buf = append(buf, lowerhex[r>>uint(s)&0xF])
}
- } else if IsPrint(r) || graphicOnly && isInGraphicList(r) {
- n := utf8.EncodeRune(runeTmp[:], r)
- buf = append(buf, runeTmp[:n]...)
- continue
- }
- switch r {
- case '\a':
- buf = append(buf, `\a`...)
- case '\b':
- buf = append(buf, `\b`...)
- case '\f':
- buf = append(buf, `\f`...)
- case '\n':
- buf = append(buf, `\n`...)
- case '\r':
- buf = append(buf, `\r`...)
- case '\t':
- buf = append(buf, `\t`...)
- case '\v':
- buf = append(buf, `\v`...)
default:
- switch {
- case r < ' ':
- buf = append(buf, `\x`...)
- buf = append(buf, lowerhex[s[0]>>4])
- buf = append(buf, lowerhex[s[0]&0xF])
- case r > utf8.MaxRune:
- r = 0xFFFD
- fallthrough
- case r < 0x10000:
- buf = append(buf, `\u`...)
- for s := 12; s >= 0; s -= 4 {
- buf = append(buf, lowerhex[r>>uint(s)&0xF])
- }
- default:
- buf = append(buf, `\U`...)
- for s := 28; s >= 0; s -= 4 {
- buf = append(buf, lowerhex[r>>uint(s)&0xF])
- }
+ buf = append(buf, `\U`...)
+ for s := 28; s >= 0; s -= 4 {
+ buf = append(buf, lowerhex[r>>uint(s)&0xF])
}
}
}
- buf = append(buf, quote)
- return string(buf)
-
+ return buf
}
// Quote returns a double-quoted Go string literal representing s. The
// AppendQuote appends a double-quoted Go string literal representing s,
// as generated by Quote, to dst and returns the extended buffer.
func AppendQuote(dst []byte, s string) []byte {
- return append(dst, Quote(s)...)
+ return appendQuotedWith(dst, s, '"', false, false)
}
// QuoteToASCII returns a double-quoted Go string literal representing s.
// AppendQuoteToASCII appends a double-quoted Go string literal representing s,
// as generated by QuoteToASCII, to dst and returns the extended buffer.
func AppendQuoteToASCII(dst []byte, s string) []byte {
- return append(dst, QuoteToASCII(s)...)
+ return appendQuotedWith(dst, s, '"', true, false)
}
// QuoteToGraphic returns a double-quoted Go string literal representing s.
// AppendQuoteToGraphic appends a double-quoted Go string literal representing s,
// as generated by QuoteToGraphic, to dst and returns the extended buffer.
func AppendQuoteToGraphic(dst []byte, s string) []byte {
- return append(dst, QuoteToGraphic(s)...)
+ return appendQuotedWith(dst, s, '"', false, true)
}
// QuoteRune returns a single-quoted Go character literal representing the
// rune. The returned string uses Go escape sequences (\t, \n, \xFF, \u0100)
// for control characters and non-printable characters as defined by IsPrint.
func QuoteRune(r rune) string {
- // TODO: avoid the allocation here.
- return quoteWith(string(r), '\'', false, false)
+ return quoteRuneWith(r, '\'', false, false)
}
// AppendQuoteRune appends a single-quoted Go character literal representing the rune,
// as generated by QuoteRune, to dst and returns the extended buffer.
func AppendQuoteRune(dst []byte, r rune) []byte {
- return append(dst, QuoteRune(r)...)
+ return appendQuotedRuneWith(dst, r, '\'', false, false)
}
// QuoteRuneToASCII returns a single-quoted Go character literal representing
// \u0100) for non-ASCII characters and non-printable characters as defined
// by IsPrint.
func QuoteRuneToASCII(r rune) string {
- // TODO: avoid the allocation here.
- return quoteWith(string(r), '\'', true, false)
+ return quoteRuneWith(r, '\'', true, false)
}
// AppendQuoteRuneToASCII appends a single-quoted Go character literal representing the rune,
// as generated by QuoteRuneToASCII, to dst and returns the extended buffer.
func AppendQuoteRuneToASCII(dst []byte, r rune) []byte {
- return append(dst, QuoteRuneToASCII(r)...)
+ return appendQuotedRuneWith(dst, r, '\'', true, false)
}
// QuoteRuneToGraphic returns a single-quoted Go character literal representing
// \u0100) for non-ASCII characters and non-printable characters as defined
// by IsGraphic.
func QuoteRuneToGraphic(r rune) string {
- // TODO: avoid the allocation here.
- return quoteWith(string(r), '\'', false, true)
+ return quoteRuneWith(r, '\'', false, true)
}
// AppendQuoteRuneToGraphic appends a single-quoted Go character literal representing the rune,
// as generated by QuoteRuneToGraphic, to dst and returns the extended buffer.
func AppendQuoteRuneToGraphic(dst []byte, r rune) []byte {
- return append(dst, QuoteRuneToGraphic(r)...)
+ return appendQuotedRuneWith(dst, r, '\'', false, true)
}
// CanBackquote reports whether the string s can be represented
}
}
+func BenchmarkQuote(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Quote("\a\b\f\r\n\t\v\a\b\f\r\n\t\v\a\b\f\r\n\t\v")
+ }
+}
+
+func BenchmarkQuoteRune(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ QuoteRune('\a')
+ }
+}
+
+var benchQuoteBuf []byte
+
+func BenchmarkAppendQuote(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ benchQuoteBuf = AppendQuote(benchQuoteBuf[:0], "\a\b\f\r\n\t\v\a\b\f\r\n\t\v\a\b\f\r\n\t\v")
+ }
+}
+
+var benchQuoteRuneBuf []byte
+
+func BenchmarkAppendQuoteRune(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ benchQuoteRuneBuf = AppendQuoteRune(benchQuoteRuneBuf[:0], '\a')
+ }
+}
+
type quoteRuneTest struct {
in rune
out string
--- /dev/null
+// 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.
+
+#include "textflag.h"
+
+#define DMB_ISH_7 \
+ MOVB runtime·goarm(SB), R11; \
+ CMP $7, R11; \
+ BLT 2(PC); \
+ WORD $0xf57ff05b // dmb ish
+
+// Plan9/ARM atomic operations.
+// TODO(minux): this only supports ARMv6K or higher.
+
+TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0
+ B ·CompareAndSwapUint32(SB)
+
+TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0
+ B ·armCompareAndSwapUint32(SB)
+
+TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0
+ B ·CompareAndSwapUint32(SB)
+
+TEXT ·AddInt32(SB),NOSPLIT,$0
+ B ·AddUint32(SB)
+
+TEXT ·AddUint32(SB),NOSPLIT,$0
+ B ·armAddUint32(SB)
+
+TEXT ·AddUintptr(SB),NOSPLIT,$0
+ B ·AddUint32(SB)
+
+TEXT ·SwapInt32(SB),NOSPLIT,$0
+ B ·SwapUint32(SB)
+
+TEXT ·SwapUint32(SB),NOSPLIT,$0
+ B ·armSwapUint32(SB)
+
+TEXT ·SwapUintptr(SB),NOSPLIT,$0
+ B ·SwapUint32(SB)
+
+TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0
+ B ·CompareAndSwapUint64(SB)
+
+TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$-4
+ B ·armCompareAndSwapUint64(SB)
+
+TEXT ·AddInt64(SB),NOSPLIT,$0
+ B ·addUint64(SB)
+
+TEXT ·AddUint64(SB),NOSPLIT,$0
+ B ·addUint64(SB)
+
+TEXT ·SwapInt64(SB),NOSPLIT,$0
+ B ·swapUint64(SB)
+
+TEXT ·SwapUint64(SB),NOSPLIT,$0
+ B ·swapUint64(SB)
+
+TEXT ·LoadInt32(SB),NOSPLIT,$0
+ B ·LoadUint32(SB)
+
+TEXT ·LoadUint32(SB),NOSPLIT,$0-8
+ MOVW addr+0(FP), R1
+load32loop:
+ LDREX (R1), R2 // loads R2
+ STREX R2, (R1), R0 // stores R2
+ CMP $0, R0
+ BNE load32loop
+ MOVW R2, val+4(FP)
+ DMB_ISH_7
+ RET
+
+TEXT ·LoadInt64(SB),NOSPLIT,$0
+ B ·loadUint64(SB)
+
+TEXT ·LoadUint64(SB),NOSPLIT,$0
+ B ·loadUint64(SB)
+
+TEXT ·LoadUintptr(SB),NOSPLIT,$0
+ B ·LoadUint32(SB)
+
+TEXT ·LoadPointer(SB),NOSPLIT,$0
+ B ·LoadUint32(SB)
+
+TEXT ·StoreInt32(SB),NOSPLIT,$0
+ B ·StoreUint32(SB)
+
+TEXT ·StoreUint32(SB),NOSPLIT,$0-8
+ MOVW addr+0(FP), R1
+ MOVW val+4(FP), R2
+ DMB_ISH_7
+storeloop:
+ LDREX (R1), R4 // loads R4
+ STREX R2, (R1), R0 // stores R2
+ CMP $0, R0
+ BNE storeloop
+ RET
+
+TEXT ·StoreInt64(SB),NOSPLIT,$0
+ B ·storeUint64(SB)
+
+TEXT ·StoreUint64(SB),NOSPLIT,$0
+ B ·storeUint64(SB)
+
+TEXT ·StoreUintptr(SB),NOSPLIT,$0
+ B ·StoreUint32(SB)
{complex(0, 0), complex(1, 2), complex(3, 4), complex(5, 6)},
}
p := 4 * runtime.GOMAXPROCS(0)
+ N := int(1e5)
+ if testing.Short() {
+ p /= 2
+ N = 1e3
+ }
for _, test := range tests {
var v Value
done := make(chan bool)
go func() {
r := rand.New(rand.NewSource(rand.Int63()))
loop:
- for j := 0; j < 1e5; j++ {
+ for j := 0; j < N; j++ {
x := test[r.Intn(len(test))]
v.Store(x)
x = v.Load()
// func Syscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr);
// Trap # in AX, args in BX CX DX SI DI, return in AX
-// Most linux systems use glibc's dynamic linker, which puts the
-// __kernel_vsyscall vdso helper at 0x10(GS) for easy access from position
-// independent code and setldt in runtime does the same in the statically
-// linked case. Android, however, uses bionic's dynamic linker, which does not
-// save the helper anywhere, and so the only way to invoke a syscall from
-// position independent code is boring old int $0x80 (which is also what
-// bionic's syscall wrappers use).
-#ifdef GOOS_android
+// See ../runtime/sys_linux_386.s for the reason why we always use int 0x80
+// instead of the glibc-specific "CALL 0x10(GS)".
#define INVOKE_SYSCALL INT $0x80
-#else
-#define INVOKE_SYSCALL CALL 0x10(GS)
-#endif
TEXT ·Syscall(SB),NOSPLIT,$0-28
CALL runtime·entersyscall(SB)
--- /dev/null
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+#define SYS_SEEK 39 /* from zsysnum_plan9.go */
+
+// System call support for plan9 on arm
+
+TEXT sysresult<>(SB),NOSPLIT,$12
+ MOVW $runtime·emptystring+0(SB), R2
+ CMP $-1, R0
+ B.NE ok
+ MOVW R1, save-4(SP)
+ BL runtime·errstr(SB)
+ MOVW save-4(SP), R1
+ MOVW $err-12(SP), R2
+ok:
+ MOVM.IA (R2), [R3-R4]
+ MOVM.IA [R3-R4], (R1)
+ RET
+
+//func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err ErrorString)
+TEXT ·Syscall(SB),NOSPLIT,$0
+ BL runtime·entersyscall(SB)
+ MOVW trap+0(FP), R0 // syscall num
+ MOVM.IA.W (R13),[R1-R2] // pop LR and caller's LR
+ SWI 0
+ MOVM.DB.W [R1-R2],(R13) // push LR and caller's LR
+ MOVW $0, R2
+ MOVW $r1+16(FP), R1
+ MOVM.IA.W [R0,R2], (R1)
+ BL sysresult<>(SB)
+ BL runtime·exitsyscall(SB)
+ RET
+
+//func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err ErrorString)
+// Actually Syscall5 but the rest of the code expects it to be named Syscall6.
+TEXT ·Syscall6(SB),NOSPLIT,$0
+ BL runtime·entersyscall(SB)
+ MOVW trap+0(FP), R0 // syscall num
+ MOVM.IA.W (R13),[R1-R2] // pop LR and caller's LR
+ SWI 0
+ MOVM.DB.W [R1-R2],(R13) // push LR and caller's LR
+ MOVW $0, R1
+ MOVW $r1+28(FP), R1
+ MOVM.IA.W [R0,R2], (R1)
+ BL sysresult<>(SB)
+ BL runtime·exitsyscall(SB)
+ RET
+
+//func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
+TEXT ·RawSyscall(SB),NOSPLIT,$0
+ MOVW trap+0(FP), R0 // syscall num
+ MOVM.IA.W (R13),[R1] // pop caller's LR
+ SWI 0
+ MOVM.DB.W [R1],(R13) // push caller's LR
+ MOVW R0, r1+16(FP)
+ MOVW R0, r2+20(FP)
+ MOVW R0, err+24(FP)
+ RET
+
+//func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
+// Actually RawSyscall5 but the rest of the code expects it to be named RawSyscall6.
+TEXT ·RawSyscall6(SB),NOSPLIT,$0
+ MOVW trap+0(FP), R0 // syscall num
+ MOVM.IA.W (R13),[R1] // pop caller's LR
+ SWI 0
+ MOVM.DB.W [R1],(R13) // push caller's LR
+ MOVW R0, r1+28(FP)
+ MOVW R0, r2+32(FP)
+ MOVW R0, err+36(FP)
+ RET
+
+//func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
+TEXT ·seek(SB),NOSPLIT,$0
+ MOVW $newoffset_lo+20(FP), R5
+ MOVW R5, placeholder+0(FP) //placeholder = dest for return value
+ MOVW $SYS_SEEK, R0 // syscall num
+ MOVM.IA.W (R13),[R1] // pop LR
+ SWI 0
+ MOVM.DB.W [R1],(R13) // push LR
+ CMP $-1, R0
+ MOVW.EQ R0, 0(R5)
+ MOVW.EQ R0, 4(R5)
+ MOVW $err+28(FP), R1
+ BL sysresult<>(SB)
+ RET
+
+//func exit(code int)
+// Import runtime·exit for cleanly exiting.
+TEXT ·exit(SB),NOSPLIT,$4
+ MOVW code+0(FP), R0
+ MOVW R0, e-4(SP)
+ BL runtime·exit(SB)
+ RET
//
// - The kernel form appends leading bytes to the prefix field
// to make the <length, prefix> tuple to be conformed with
- // the routing messeage boundary
+ // the routing message boundary
l := int(rsaAlignOf(int(b[0])))
if len(b) < l {
return nil, EINVAL
--- /dev/null
+// mksyscall.pl -l32 -plan9 syscall_plan9.go
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+// +build arm,plan9
+
+package syscall
+
+import "unsafe"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fd2path(fd int, buf []byte) (err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pipe(p *[2]int32) (err error) {
+ r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func await(s []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(s) > 0 {
+ _p0 = unsafe.Pointer(&s[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func open(path string, mode int) (fd int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ use(unsafe.Pointer(_p0))
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func create(path string, mode int, perm uint32) (fd int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
+ use(unsafe.Pointer(_p0))
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func remove(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ use(unsafe.Pointer(_p0))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func stat(path string, edir []byte) (n int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(edir) > 0 {
+ _p1 = unsafe.Pointer(&edir[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
+ use(unsafe.Pointer(_p0))
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func bind(name string, old string, flag int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(old)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag))
+ use(unsafe.Pointer(_p0))
+ use(unsafe.Pointer(_p1))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mount(fd int, afd int, old string, flag int, aname string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(old)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(aname)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0)
+ use(unsafe.Pointer(_p0))
+ use(unsafe.Pointer(_p1))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func wstat(path string, edir []byte) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(edir) > 0 {
+ _p1 = unsafe.Pointer(&edir[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
+ use(unsafe.Pointer(_p0))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func chdir(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ use(unsafe.Pointer(_p0))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup(oldfd int, newfd int) (fd int, err error) {
+ r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0)
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pread(fd int, p []byte, offset int64) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Close(fd int) (err error) {
+ r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstat(fd int, edir []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(edir) > 0 {
+ _p0 = unsafe.Pointer(&edir[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fwstat(fd int, edir []byte) (err error) {
+ var _p0 unsafe.Pointer
+ if len(edir) > 0 {
+ _p0 = unsafe.Pointer(&edir[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
"strings"
)
-var defaultMaxCount *int = flag.Int("quickchecks", 100, "The default number of iterations for each check")
+var defaultMaxCount = flag.Int("quickchecks", 100, "The default number of iterations for each check")
// A Generator can generate random values of its own type.
type Generator interface {
case reflect.Uintptr:
v.SetUint(uint64(randInt64(rand)))
case reflect.Map:
- numElems := rand.Intn(size)
- v.Set(reflect.MakeMap(concrete))
- for i := 0; i < numElems; i++ {
- key, ok1 := sizedValue(concrete.Key(), rand, size)
- value, ok2 := sizedValue(concrete.Elem(), rand, size)
- if !ok1 || !ok2 {
- return reflect.Value{}, false
+ if generateNilValue(rand) {
+ v.Set(reflect.Zero(concrete)) // Generate nil map.
+ } else {
+ numElems := rand.Intn(size)
+ v.Set(reflect.MakeMap(concrete))
+ for i := 0; i < numElems; i++ {
+ key, ok1 := sizedValue(concrete.Key(), rand, size)
+ value, ok2 := sizedValue(concrete.Elem(), rand, size)
+ if !ok1 || !ok2 {
+ return reflect.Value{}, false
+ }
+ v.SetMapIndex(key, value)
}
- v.SetMapIndex(key, value)
}
case reflect.Ptr:
- if rand.Intn(size) == 0 {
+ if generateNilValue(rand) {
v.Set(reflect.Zero(concrete)) // Generate nil pointer.
} else {
elem, ok := sizedValue(concrete.Elem(), rand, size)
v.Elem().Set(elem)
}
case reflect.Slice:
- numElems := rand.Intn(size)
- sizeLeft := size - numElems
- v.Set(reflect.MakeSlice(concrete, numElems, numElems))
- for i := 0; i < numElems; i++ {
- elem, ok := sizedValue(concrete.Elem(), rand, sizeLeft)
- if !ok {
- return reflect.Value{}, false
+ if generateNilValue(rand) {
+ v.Set(reflect.Zero(concrete)) // Generate nil slice.
+ } else {
+ slCap := rand.Intn(size)
+ slLen := rand.Intn(slCap + 1)
+ sizeLeft := size - slCap
+ v.Set(reflect.MakeSlice(concrete, slLen, slCap))
+ for i := 0; i < slLen; i++ {
+ elem, ok := sizedValue(concrete.Elem(), rand, sizeLeft)
+ if !ok {
+ return reflect.Value{}, false
+ }
+ v.Index(i).Set(elem)
}
- v.Index(i).Set(elem)
}
case reflect.Array:
for i := 0; i < v.Len(); i++ {
}
return strings.Join(s, ", ")
}
+
+func generateNilValue(r *rand.Rand) bool { return r.Intn(20) == 0 }
f := func(a A) bool { return true }
Check(f, nil)
}
-
-// Some serialization formats (e.g. encoding/pem) cannot distinguish
-// between a nil and an empty map or slice, so avoid generating the
-// zero value for these.
-func TestNonZeroSliceAndMap(t *testing.T) {
- type Q struct {
- M map[int]int
- S []int
- }
- f := func(q Q) bool {
- return q.M != nil && q.S != nil
- }
- err := Check(f, nil)
- if err != nil {
- t.Fatal(err)
- }
-}
// a b c d.
// 123 12345 1234567 123456789.
}
+
+func Example_elastic() {
+ // Observe how the b's and the d's, despite appearing in the
+ // second cell of each line, belong to different columns.
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, '.', tabwriter.AlignRight|tabwriter.Debug)
+ fmt.Fprintln(w, "a\tb\tc")
+ fmt.Fprintln(w, "aa\tbb\tcc")
+ fmt.Fprintln(w, "aaa\t") // trailing tab
+ fmt.Fprintln(w, "aaaa\tdddd\teeee")
+ w.Flush()
+
+ // output:
+ // ....a|..b|c
+ // ...aa|.bb|cc
+ // ..aaa|
+ // .aaaa|.dddd|eeee
+}
+
+func Example_trailingTab() {
+ // Observe that the third line has no trailing tab,
+ // so its final cell is not part of an aligned column.
+ const padding = 3
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, padding, '-', tabwriter.AlignRight|tabwriter.Debug)
+ fmt.Fprintln(w, "a\tb\taligned\t")
+ fmt.Fprintln(w, "aa\tbb\taligned\t")
+ fmt.Fprintln(w, "aaa\tbbb\tunaligned") // no trailing tab
+ fmt.Fprintln(w, "aaaa\tbbbb\taligned\t")
+ w.Flush()
+
+ // output:
+ // ------a|------b|---aligned|
+ // -----aa|-----bb|---aligned|
+ // ----aaa|----bbb|unaligned
+ // ---aaaa|---bbbb|---aligned|
+}
// A Writer is a filter that inserts padding around tab-delimited
// columns in its input to align them in the output.
//
-// The Writer treats incoming bytes as UTF-8 encoded text consisting
-// of cells terminated by (horizontal or vertical) tabs or line
-// breaks (newline or formfeed characters). Cells in adjacent lines
-// constitute a column. The Writer inserts padding as needed to
-// make all cells in a column have the same width, effectively
-// aligning the columns. It assumes that all characters have the
-// same width except for tabs for which a tabwidth must be specified.
-// Note that cells are tab-terminated, not tab-separated: trailing
-// non-tab text at the end of a line does not form a column cell.
+// The Writer treats incoming bytes as UTF-8-encoded text consisting
+// of cells terminated by horizontal ('\t') or vertical ('\v') tabs,
+// and newline ('\n') or formfeed ('\f') characters; both newline and
+// formfeed act as line breaks.
+//
+// Tab-terminated cells in contiguous lines constitute a column. The
+// Writer inserts padding as needed to make all cells in a column have
+// the same width, effectively aligning the columns. It assumes that
+// all characters have the same width, except for tabs for which a
+// tabwidth must be specified. Column cells must be tab-terminated, not
+// tab-separated: non-tab terminated trailing text at the end of a line
+// forms a cell but that cell is not part of an aligned column.
+// For instance, in this example (where | stands for a horizontal tab):
+//
+// aaaa|bbb|d
+// aa |b |dd
+// a |
+// aa |cccc|eee
+//
+// the b and c are in distinct columns (the b column is not contiguous
+// all the way). The d and e are not in a column at all (there's no
+// terminating tab, nor would the column be contiguous).
//
// The Writer assumes that all Unicode code points have the same width;
-// this may not be true in some fonts.
+// this may not be true in some fonts or if the string contains combining
+// characters.
//
// If DiscardEmptyColumns is set, empty columns that are terminated
// entirely by vertical (or "soft") tabs are discarded. Columns
// width of the escaped text is always computed excluding the Escape
// characters.
//
-// The formfeed character ('\f') acts like a newline but it also
-// terminates all columns in the current line (effectively calling
-// Flush). Cells in the next line start new columns. Unless found
+// The formfeed character acts like a newline but it also terminates
+// all columns in the current line (effectively calling Flush). Tab-
+// terminated cells in the next line start new columns. Unless found
// inside an HTML tag or inside an escaped text segment, formfeed
// characters appear as newlines in the output.
//
package template
-// Tests for mulitple-template parsing and execution.
+// Tests for multiple-template parsing and execution.
import (
"bytes"
// If the rune is not a valid Unicode code point or does not need encoding,
// EncodeRune returns U+FFFD, U+FFFD.
func EncodeRune(r rune) (r1, r2 rune) {
- if r < surrSelf || r > maxRune || IsSurrogate(r) {
+ if r < surrSelf || r > maxRune {
return replacementChar, replacementChar
}
r -= surrSelf
n = 0
for _, v := range s {
switch {
- case v < 0, surr1 <= v && v < surr3, v > maxRune:
- v = replacementChar
- fallthrough
- case v < surrSelf:
+ case 0 <= v && v < surr1, surr3 <= v && v < surrSelf:
+ // normal rune
a[n] = uint16(v)
n++
- default:
+ case surrSelf <= v && v <= maxRune:
+ // needs surrogate sequence
r1, r2 := EncodeRune(v)
a[n] = uint16(r1)
a[n+1] = uint16(r2)
n += 2
+ default:
+ a[n] = uint16(replacementChar)
+ n++
}
}
- return a[0:n]
+ return a[:n]
}
// Decode returns the Unicode code point sequence represented
package main
func f() {
- v := 1 << 1025; // ERROR "overflow|stupid shift"
+ v := 1 << 1025; // ERROR "overflow|shift count too large"
_ = v
}
package a
import"" // ERROR "import path is empty"
-var? // ERROR "unexpected \?"
+var? // ERROR "illegal character U\+003F '\?'"
var x int // ERROR "unexpected var" "cannot declare name"
--- /dev/null
+// errorcheck
+
+// Copyright 2016 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 f
+
+import /* // ERROR "import path" */ `
+bogus`
+
+func f(x int /* // ERROR "unexpected semicolon"
+
+*/)