parent
f2034d064c
commit
f72f416913
|
@ -6,12 +6,14 @@
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import "os"
|
||||||
"net"
|
|
||||||
)
|
// Issue 481: closures and var declarations
|
||||||
|
// with multiple variables assigned from one
|
||||||
|
// function call.
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var listen, _ = net.Listen("tcp", "127.0.0.1:0")
|
var listen, _ = Listen("tcp", "127.0.0.1:0")
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
|
@ -20,6 +22,31 @@ func main() {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var conn, _ = net.Dial("tcp", "", listen.Addr().String())
|
var conn, _ = Dial("tcp", "", listen.Addr().String())
|
||||||
_ = conn
|
_ = conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simulated net interface to exercise bug
|
||||||
|
// without involving a real network.
|
||||||
|
type T chan int
|
||||||
|
|
||||||
|
var global T
|
||||||
|
|
||||||
|
func Listen(x, y string) (T, string) {
|
||||||
|
global = make(chan int)
|
||||||
|
return global, y
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t T) Addr() os.Error {
|
||||||
|
return os.ErrorString("stringer")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t T) Accept() (int, string) {
|
||||||
|
return <-t, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func Dial(x, y, z string) (int, string) {
|
||||||
|
global <- 1
|
||||||
|
return 0, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
31d7feb9281b
|
342e3b11f21a
|
||||||
|
|
||||||
The first line of this file holds the Mercurial revision number of the
|
The first line of this file holds the Mercurial revision number of the
|
||||||
last merge done from the master library sources.
|
last merge done from the master library sources.
|
||||||
|
|
|
@ -657,6 +657,7 @@ endif # !LIBGO_IS_LINUX
|
||||||
endif # !LIBGO_IS_RTEMS
|
endif # !LIBGO_IS_RTEMS
|
||||||
|
|
||||||
go_net_files = \
|
go_net_files = \
|
||||||
|
go/net/cgo_stub.go \
|
||||||
go/net/dial.go \
|
go/net/dial.go \
|
||||||
go/net/dnsclient.go \
|
go/net/dnsclient.go \
|
||||||
go/net/dnsconfig.go \
|
go/net/dnsconfig.go \
|
||||||
|
@ -664,10 +665,12 @@ go_net_files = \
|
||||||
$(go_net_newpollserver_file) \
|
$(go_net_newpollserver_file) \
|
||||||
go/net/fd.go \
|
go/net/fd.go \
|
||||||
$(go_net_fd_os_file) \
|
$(go_net_fd_os_file) \
|
||||||
|
go/net/file.go \
|
||||||
go/net/hosts.go \
|
go/net/hosts.go \
|
||||||
go/net/ip.go \
|
go/net/ip.go \
|
||||||
go/net/iprawsock.go \
|
go/net/iprawsock.go \
|
||||||
go/net/ipsock.go \
|
go/net/ipsock.go \
|
||||||
|
go/net/lookup.go \
|
||||||
go/net/net.go \
|
go/net/net.go \
|
||||||
go/net/parse.go \
|
go/net/parse.go \
|
||||||
go/net/pipe.go \
|
go/net/pipe.go \
|
||||||
|
@ -1095,6 +1098,7 @@ go_go_ast_files = \
|
||||||
go/go/ast/ast.go \
|
go/go/ast/ast.go \
|
||||||
go/go/ast/filter.go \
|
go/go/ast/filter.go \
|
||||||
go/go/ast/print.go \
|
go/go/ast/print.go \
|
||||||
|
go/go/ast/resolve.go \
|
||||||
go/go/ast/scope.go \
|
go/go/ast/scope.go \
|
||||||
go/go/ast/walk.go
|
go/go/ast/walk.go
|
||||||
go_go_doc_files = \
|
go_go_doc_files = \
|
||||||
|
@ -2327,8 +2331,8 @@ exp/eval/check: $(CHECK_DEPS)
|
||||||
$(CHECK)
|
$(CHECK)
|
||||||
.PHONY: exp/eval/check
|
.PHONY: exp/eval/check
|
||||||
|
|
||||||
go/ast.lo: $(go_go_ast_files) bytes.gox fmt.gox go/token.gox io.gox os.gox \
|
go/ast.lo: $(go_go_ast_files) bytes.gox fmt.gox go/scanner.gox go/token.gox \
|
||||||
reflect.gox unicode.gox utf8.gox
|
io.gox os.gox reflect.gox unicode.gox utf8.gox
|
||||||
$(BUILDPACKAGE)
|
$(BUILDPACKAGE)
|
||||||
go/ast/check: $(CHECK_DEPS)
|
go/ast/check: $(CHECK_DEPS)
|
||||||
@$(MKDIR_P) go/ast
|
@$(MKDIR_P) go/ast
|
||||||
|
|
|
@ -1039,6 +1039,7 @@ go_mime_files = \
|
||||||
@LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go
|
@LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go
|
||||||
@LIBGO_IS_RTEMS_TRUE@go_net_newpollserver_file = go/net/newpollserver_rtems.go
|
@LIBGO_IS_RTEMS_TRUE@go_net_newpollserver_file = go/net/newpollserver_rtems.go
|
||||||
go_net_files = \
|
go_net_files = \
|
||||||
|
go/net/cgo_stub.go \
|
||||||
go/net/dial.go \
|
go/net/dial.go \
|
||||||
go/net/dnsclient.go \
|
go/net/dnsclient.go \
|
||||||
go/net/dnsconfig.go \
|
go/net/dnsconfig.go \
|
||||||
|
@ -1046,10 +1047,12 @@ go_net_files = \
|
||||||
$(go_net_newpollserver_file) \
|
$(go_net_newpollserver_file) \
|
||||||
go/net/fd.go \
|
go/net/fd.go \
|
||||||
$(go_net_fd_os_file) \
|
$(go_net_fd_os_file) \
|
||||||
|
go/net/file.go \
|
||||||
go/net/hosts.go \
|
go/net/hosts.go \
|
||||||
go/net/ip.go \
|
go/net/ip.go \
|
||||||
go/net/iprawsock.go \
|
go/net/iprawsock.go \
|
||||||
go/net/ipsock.go \
|
go/net/ipsock.go \
|
||||||
|
go/net/lookup.go \
|
||||||
go/net/net.go \
|
go/net/net.go \
|
||||||
go/net/parse.go \
|
go/net/parse.go \
|
||||||
go/net/pipe.go \
|
go/net/pipe.go \
|
||||||
|
@ -1483,6 +1486,7 @@ go_go_ast_files = \
|
||||||
go/go/ast/ast.go \
|
go/go/ast/ast.go \
|
||||||
go/go/ast/filter.go \
|
go/go/ast/filter.go \
|
||||||
go/go/ast/print.go \
|
go/go/ast/print.go \
|
||||||
|
go/go/ast/resolve.go \
|
||||||
go/go/ast/scope.go \
|
go/go/ast/scope.go \
|
||||||
go/go/ast/walk.go
|
go/go/ast/walk.go
|
||||||
|
|
||||||
|
@ -4747,8 +4751,8 @@ exp/eval/check: $(CHECK_DEPS)
|
||||||
$(CHECK)
|
$(CHECK)
|
||||||
.PHONY: exp/eval/check
|
.PHONY: exp/eval/check
|
||||||
|
|
||||||
go/ast.lo: $(go_go_ast_files) bytes.gox fmt.gox go/token.gox io.gox os.gox \
|
go/ast.lo: $(go_go_ast_files) bytes.gox fmt.gox go/scanner.gox go/token.gox \
|
||||||
reflect.gox unicode.gox utf8.gox
|
io.gox os.gox reflect.gox unicode.gox utf8.gox
|
||||||
$(BUILDPACKAGE)
|
$(BUILDPACKAGE)
|
||||||
go/ast/check: $(CHECK_DEPS)
|
go/ast/check: $(CHECK_DEPS)
|
||||||
@$(MKDIR_P) go/ast
|
@$(MKDIR_P) go/ast
|
||||||
|
|
|
@ -150,5 +150,8 @@ testLoop:
|
||||||
t.Errorf("test %d: Incorrect result: (-=expected, +=actual)\n%v",
|
t.Errorf("test %d: Incorrect result: (-=expected, +=actual)\n%v",
|
||||||
i, bytediff(expected, actual))
|
i, bytediff(expected, actual))
|
||||||
}
|
}
|
||||||
|
if testing.Short() { // The second test is expensive.
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -389,6 +389,11 @@ func parseSequenceOf(bytes []byte, sliceType *reflect.SliceType, elemType reflec
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// We pretend that GENERAL STRINGs are PRINTABLE STRINGs so
|
||||||
|
// that a sequence of them can be parsed into a []string.
|
||||||
|
if t.tag == tagGeneralString {
|
||||||
|
t.tag = tagPrintableString
|
||||||
|
}
|
||||||
if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag {
|
if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag {
|
||||||
err = StructuralError{"sequence tag mismatch"}
|
err = StructuralError{"sequence tag mismatch"}
|
||||||
return
|
return
|
||||||
|
@ -516,7 +521,11 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if params.explicit {
|
if params.explicit {
|
||||||
if t.class == classContextSpecific && t.tag == *params.tag && (t.length == 0 || t.isCompound) {
|
expectedClass := classContextSpecific
|
||||||
|
if params.application {
|
||||||
|
expectedClass = classApplication
|
||||||
|
}
|
||||||
|
if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) {
|
||||||
if t.length > 0 {
|
if t.length > 0 {
|
||||||
t, offset, err = parseTagAndLength(bytes, offset)
|
t, offset, err = parseTagAndLength(bytes, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -551,6 +560,10 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
|
||||||
if universalTag == tagPrintableString && t.tag == tagIA5String {
|
if universalTag == tagPrintableString && t.tag == tagIA5String {
|
||||||
universalTag = tagIA5String
|
universalTag = tagIA5String
|
||||||
}
|
}
|
||||||
|
// Likewise for GeneralString
|
||||||
|
if universalTag == tagPrintableString && t.tag == tagGeneralString {
|
||||||
|
universalTag = tagGeneralString
|
||||||
|
}
|
||||||
|
|
||||||
// Special case for time: UTCTime and GeneralizedTime both map to the
|
// Special case for time: UTCTime and GeneralizedTime both map to the
|
||||||
// Go type time.Time.
|
// Go type time.Time.
|
||||||
|
@ -566,6 +579,11 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
|
||||||
expectedTag = *params.tag
|
expectedTag = *params.tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !params.explicit && params.application && params.tag != nil {
|
||||||
|
expectedClass = classApplication
|
||||||
|
expectedTag = *params.tag
|
||||||
|
}
|
||||||
|
|
||||||
// We have unwrapped any explicit tagging at this point.
|
// We have unwrapped any explicit tagging at this point.
|
||||||
if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType {
|
if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType {
|
||||||
// Tags don't match. Again, it could be an optional element.
|
// Tags don't match. Again, it could be an optional element.
|
||||||
|
@ -701,6 +719,12 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
|
||||||
v, err = parseIA5String(innerBytes)
|
v, err = parseIA5String(innerBytes)
|
||||||
case tagT61String:
|
case tagT61String:
|
||||||
v, err = parseT61String(innerBytes)
|
v, err = parseT61String(innerBytes)
|
||||||
|
case tagGeneralString:
|
||||||
|
// GeneralString is specified in ISO-2022/ECMA-35,
|
||||||
|
// A brief review suggests that it includes structures
|
||||||
|
// that allow the encoding to change midstring and
|
||||||
|
// such. We give up and pass it as an 8-bit string.
|
||||||
|
v, err = parseT61String(innerBytes)
|
||||||
default:
|
default:
|
||||||
err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)}
|
err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)}
|
||||||
}
|
}
|
||||||
|
@ -776,8 +800,14 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
|
||||||
// Other ASN.1 types are not supported; if it encounters them,
|
// Other ASN.1 types are not supported; if it encounters them,
|
||||||
// Unmarshal returns a parse error.
|
// Unmarshal returns a parse error.
|
||||||
func Unmarshal(b []byte, val interface{}) (rest []byte, err os.Error) {
|
func Unmarshal(b []byte, val interface{}) (rest []byte, err os.Error) {
|
||||||
|
return UnmarshalWithParams(b, val, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalWithParams allows field parameters to be specified for the
|
||||||
|
// top-level element. The form of the params is the same as the field tags.
|
||||||
|
func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err os.Error) {
|
||||||
v := reflect.NewValue(val).(*reflect.PtrValue).Elem()
|
v := reflect.NewValue(val).(*reflect.PtrValue).Elem()
|
||||||
offset, err := parseField(v, b, 0, fieldParameters{})
|
offset, err := parseField(v, b, 0, parseFieldParameters(params))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,11 +249,12 @@ var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParame
|
||||||
{"printable", fieldParameters{stringType: tagPrintableString}},
|
{"printable", fieldParameters{stringType: tagPrintableString}},
|
||||||
{"optional", fieldParameters{optional: true}},
|
{"optional", fieldParameters{optional: true}},
|
||||||
{"explicit", fieldParameters{explicit: true, tag: new(int)}},
|
{"explicit", fieldParameters{explicit: true, tag: new(int)}},
|
||||||
|
{"application", fieldParameters{application: true, tag: new(int)}},
|
||||||
{"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}},
|
{"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}},
|
||||||
{"default:42", fieldParameters{defaultValue: newInt64(42)}},
|
{"default:42", fieldParameters{defaultValue: newInt64(42)}},
|
||||||
{"tag:17", fieldParameters{tag: newInt(17)}},
|
{"tag:17", fieldParameters{tag: newInt(17)}},
|
||||||
{"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}},
|
{"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}},
|
||||||
{"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, newInt64(42), newInt(17), 0, false}},
|
{"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, false, newInt64(42), newInt(17), 0, false}},
|
||||||
{"set", fieldParameters{set: true}},
|
{"set", fieldParameters{set: true}},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ const (
|
||||||
tagIA5String = 22
|
tagIA5String = 22
|
||||||
tagUTCTime = 23
|
tagUTCTime = 23
|
||||||
tagGeneralizedTime = 24
|
tagGeneralizedTime = 24
|
||||||
|
tagGeneralString = 27
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -67,7 +68,8 @@ type tagAndLength struct {
|
||||||
// fieldParameters is the parsed representation of tag string from a structure field.
|
// fieldParameters is the parsed representation of tag string from a structure field.
|
||||||
type fieldParameters struct {
|
type fieldParameters struct {
|
||||||
optional bool // true iff the field is OPTIONAL
|
optional bool // true iff the field is OPTIONAL
|
||||||
explicit bool // true iff and EXPLICIT tag is in use.
|
explicit bool // true iff an EXPLICIT tag is in use.
|
||||||
|
application bool // true iff an APPLICATION tag is in use.
|
||||||
defaultValue *int64 // a default value for INTEGER typed fields (maybe nil).
|
defaultValue *int64 // a default value for INTEGER typed fields (maybe nil).
|
||||||
tag *int // the EXPLICIT or IMPLICIT tag (maybe nil).
|
tag *int // the EXPLICIT or IMPLICIT tag (maybe nil).
|
||||||
stringType int // the string tag to use when marshaling.
|
stringType int // the string tag to use when marshaling.
|
||||||
|
@ -89,7 +91,6 @@ func parseFieldParameters(str string) (ret fieldParameters) {
|
||||||
ret.explicit = true
|
ret.explicit = true
|
||||||
if ret.tag == nil {
|
if ret.tag == nil {
|
||||||
ret.tag = new(int)
|
ret.tag = new(int)
|
||||||
*ret.tag = 0
|
|
||||||
}
|
}
|
||||||
case part == "ia5":
|
case part == "ia5":
|
||||||
ret.stringType = tagIA5String
|
ret.stringType = tagIA5String
|
||||||
|
@ -109,6 +110,11 @@ func parseFieldParameters(str string) (ret fieldParameters) {
|
||||||
}
|
}
|
||||||
case part == "set":
|
case part == "set":
|
||||||
ret.set = true
|
ret.set = true
|
||||||
|
case part == "application":
|
||||||
|
ret.application = true
|
||||||
|
if ret.tag == nil {
|
||||||
|
ret.tag = new(int)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
|
@ -716,18 +716,25 @@ var composites = []string{
|
||||||
|
|
||||||
|
|
||||||
func TestProbablyPrime(t *testing.T) {
|
func TestProbablyPrime(t *testing.T) {
|
||||||
|
nreps := 20
|
||||||
|
if testing.Short() {
|
||||||
|
nreps = 1
|
||||||
|
}
|
||||||
for i, s := range primes {
|
for i, s := range primes {
|
||||||
p, _ := new(Int).SetString(s, 10)
|
p, _ := new(Int).SetString(s, 10)
|
||||||
if !ProbablyPrime(p, 20) {
|
if !ProbablyPrime(p, nreps) {
|
||||||
t.Errorf("#%d prime found to be non-prime (%s)", i, s)
|
t.Errorf("#%d prime found to be non-prime (%s)", i, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, s := range composites {
|
for i, s := range composites {
|
||||||
c, _ := new(Int).SetString(s, 10)
|
c, _ := new(Int).SetString(s, 10)
|
||||||
if ProbablyPrime(c, 20) {
|
if ProbablyPrime(c, nreps) {
|
||||||
t.Errorf("#%d composite found to be prime (%s)", i, s)
|
t.Errorf("#%d composite found to be prime (%s)", i, s)
|
||||||
}
|
}
|
||||||
|
if testing.Short() {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -178,7 +178,11 @@ func TestBasicOperations(t *testing.T) {
|
||||||
|
|
||||||
func TestLargeStringWrites(t *testing.T) {
|
func TestLargeStringWrites(t *testing.T) {
|
||||||
var buf Buffer
|
var buf Buffer
|
||||||
for i := 3; i < 30; i += 3 {
|
limit := 30
|
||||||
|
if testing.Short() {
|
||||||
|
limit = 9
|
||||||
|
}
|
||||||
|
for i := 3; i < limit; i += 3 {
|
||||||
s := fillString(t, "TestLargeWrites (1)", &buf, "", 5, data)
|
s := fillString(t, "TestLargeWrites (1)", &buf, "", 5, data)
|
||||||
empty(t, "TestLargeStringWrites (2)", &buf, s, make([]byte, len(data)/i))
|
empty(t, "TestLargeStringWrites (2)", &buf, s, make([]byte, len(data)/i))
|
||||||
}
|
}
|
||||||
|
@ -188,7 +192,11 @@ func TestLargeStringWrites(t *testing.T) {
|
||||||
|
|
||||||
func TestLargeByteWrites(t *testing.T) {
|
func TestLargeByteWrites(t *testing.T) {
|
||||||
var buf Buffer
|
var buf Buffer
|
||||||
for i := 3; i < 30; i += 3 {
|
limit := 30
|
||||||
|
if testing.Short() {
|
||||||
|
limit = 9
|
||||||
|
}
|
||||||
|
for i := 3; i < limit; i += 3 {
|
||||||
s := fillBytes(t, "TestLargeWrites (1)", &buf, "", 5, bytes)
|
s := fillBytes(t, "TestLargeWrites (1)", &buf, "", 5, bytes)
|
||||||
empty(t, "TestLargeByteWrites (2)", &buf, s, make([]byte, len(data)/i))
|
empty(t, "TestLargeByteWrites (2)", &buf, s, make([]byte, len(data)/i))
|
||||||
}
|
}
|
||||||
|
|
|
@ -293,20 +293,10 @@ func Join(a [][]byte, sep []byte) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
b := make([]byte, n)
|
b := make([]byte, n)
|
||||||
bp := 0
|
bp := copy(b, a[0])
|
||||||
for i := 0; i < len(a); i++ {
|
for _, s := range a[1:] {
|
||||||
s := a[i]
|
bp += copy(b[bp:], sep)
|
||||||
for j := 0; j < len(s); j++ {
|
bp += copy(b[bp:], s)
|
||||||
b[bp] = s[j]
|
|
||||||
bp++
|
|
||||||
}
|
|
||||||
if i+1 < len(a) {
|
|
||||||
s = sep
|
|
||||||
for j := 0; j < len(s); j++ {
|
|
||||||
b[bp] = s[j]
|
|
||||||
bp++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,7 +201,10 @@ func TestIndexByte(t *testing.T) {
|
||||||
|
|
||||||
// test a larger buffer with different sizes and alignments
|
// test a larger buffer with different sizes and alignments
|
||||||
func TestIndexByteBig(t *testing.T) {
|
func TestIndexByteBig(t *testing.T) {
|
||||||
const n = 1024
|
var n = 1024
|
||||||
|
if testing.Short() {
|
||||||
|
n = 128
|
||||||
|
}
|
||||||
b := make([]byte, n)
|
b := make([]byte, n)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
// different start alignments
|
// different start alignments
|
||||||
|
|
|
@ -33,6 +33,9 @@ func s(n uint64) string {
|
||||||
|
|
||||||
|
|
||||||
func TestVectorNums(t *testing.T) {
|
func TestVectorNums(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
var v Vector
|
var v Vector
|
||||||
c := int(0)
|
c := int(0)
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
|
@ -51,6 +54,9 @@ func TestVectorNums(t *testing.T) {
|
||||||
|
|
||||||
|
|
||||||
func TestIntVectorNums(t *testing.T) {
|
func TestIntVectorNums(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
var v IntVector
|
var v IntVector
|
||||||
c := int(0)
|
c := int(0)
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
|
@ -69,6 +75,9 @@ func TestIntVectorNums(t *testing.T) {
|
||||||
|
|
||||||
|
|
||||||
func TestStringVectorNums(t *testing.T) {
|
func TestStringVectorNums(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
var v StringVector
|
var v StringVector
|
||||||
c := ""
|
c := ""
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
|
|
|
@ -22,6 +22,10 @@ type ctr struct {
|
||||||
// NewCTR returns a Stream which encrypts/decrypts using the given Block in
|
// NewCTR returns a Stream which encrypts/decrypts using the given Block in
|
||||||
// counter mode. The length of iv must be the same as the Block's block size.
|
// counter mode. The length of iv must be the same as the Block's block size.
|
||||||
func NewCTR(block Block, iv []byte) Stream {
|
func NewCTR(block Block, iv []byte) Stream {
|
||||||
|
if len(iv) != block.BlockSize() {
|
||||||
|
panic("cipher.NewCTR: iv length must equal block size")
|
||||||
|
}
|
||||||
|
|
||||||
return &ctr{
|
return &ctr{
|
||||||
b: block,
|
b: block,
|
||||||
ctr: dup(iv),
|
ctr: dup(iv),
|
||||||
|
|
|
@ -20,12 +20,15 @@ func testKeyGeneration(t *testing.T, c *elliptic.Curve, tag string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !c.IsOnCurve(priv.PublicKey.X, priv.PublicKey.Y) {
|
if !c.IsOnCurve(priv.PublicKey.X, priv.PublicKey.Y) {
|
||||||
t.Errorf("%s: public key invalid", tag, err)
|
t.Errorf("%s: public key invalid: %s", tag, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyGeneration(t *testing.T) {
|
func TestKeyGeneration(t *testing.T) {
|
||||||
testKeyGeneration(t, elliptic.P224(), "p224")
|
testKeyGeneration(t, elliptic.P224(), "p224")
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
testKeyGeneration(t, elliptic.P256(), "p256")
|
testKeyGeneration(t, elliptic.P256(), "p256")
|
||||||
testKeyGeneration(t, elliptic.P384(), "p384")
|
testKeyGeneration(t, elliptic.P384(), "p384")
|
||||||
testKeyGeneration(t, elliptic.P521(), "p521")
|
testKeyGeneration(t, elliptic.P521(), "p521")
|
||||||
|
@ -53,6 +56,9 @@ func testSignAndVerify(t *testing.T, c *elliptic.Curve, tag string) {
|
||||||
|
|
||||||
func TestSignAndVerify(t *testing.T) {
|
func TestSignAndVerify(t *testing.T) {
|
||||||
testSignAndVerify(t, elliptic.P224(), "p224")
|
testSignAndVerify(t, elliptic.P224(), "p224")
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
testSignAndVerify(t, elliptic.P256(), "p256")
|
testSignAndVerify(t, elliptic.P256(), "p256")
|
||||||
testSignAndVerify(t, elliptic.P384(), "p384")
|
testSignAndVerify(t, elliptic.P384(), "p384")
|
||||||
testSignAndVerify(t, elliptic.P521(), "p521")
|
testSignAndVerify(t, elliptic.P521(), "p521")
|
||||||
|
@ -214,5 +220,8 @@ func TestVectors(t *testing.T) {
|
||||||
if Verify(&pub, hashed, r, s) != test.ok {
|
if Verify(&pub, hashed, r, s) != test.ok {
|
||||||
t.Errorf("%d: bad result", i)
|
t.Errorf("%d: bad result", i)
|
||||||
}
|
}
|
||||||
|
if testing.Short() {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -297,6 +297,9 @@ func TestBaseMult(t *testing.T) {
|
||||||
if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y {
|
if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y {
|
||||||
t.Errorf("%d: bad output for k=%s: got (%x, %s), want (%s, %s)", i, e.k, x, y, e.x, e.y)
|
t.Errorf("%d: bad output for k=%s: got (%x, %s), want (%s, %s)", i, e.k, x, y, e.x, e.y)
|
||||||
}
|
}
|
||||||
|
if testing.Short() && i > 5 {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,5 +90,8 @@ func TestParse(t *testing.T) {
|
||||||
if !bytes.Equal(out, expected) {
|
if !bytes.Equal(out, expected) {
|
||||||
t.Errorf("%d: output got: %x want: %x", i, out, expected)
|
t.Errorf("%d: output got: %x want: %x", i, out, expected)
|
||||||
}
|
}
|
||||||
|
if testing.Short() {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRead(t *testing.T) {
|
func TestRead(t *testing.T) {
|
||||||
b := make([]byte, 4e6)
|
var n int = 4e6
|
||||||
|
if testing.Short() {
|
||||||
|
n = 1e5
|
||||||
|
}
|
||||||
|
b := make([]byte, n)
|
||||||
n, err := Read(b)
|
n, err := Read(b)
|
||||||
if n != len(b) || err != nil {
|
if n != len(b) || err != nil {
|
||||||
t.Fatalf("Read(buf) = %d, %s", n, err)
|
t.Fatalf("Read(buf) = %d, %s", n, err)
|
||||||
|
|
|
@ -97,7 +97,11 @@ func TestEncryptPKCS1v15(t *testing.T) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
quick.Check(tryEncryptDecrypt, nil)
|
config := new(quick.Config)
|
||||||
|
if testing.Short() {
|
||||||
|
config.MaxCount = 10
|
||||||
|
}
|
||||||
|
quick.Check(tryEncryptDecrypt, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// These test vectors were generated with `openssl rsautl -pkcs -encrypt`
|
// These test vectors were generated with `openssl rsautl -pkcs -encrypt`
|
||||||
|
|
|
@ -15,7 +15,11 @@ import (
|
||||||
func TestKeyGeneration(t *testing.T) {
|
func TestKeyGeneration(t *testing.T) {
|
||||||
random := rand.Reader
|
random := rand.Reader
|
||||||
|
|
||||||
priv, err := GenerateKey(random, 1024)
|
size := 1024
|
||||||
|
if testing.Short() {
|
||||||
|
size = 128
|
||||||
|
}
|
||||||
|
priv, err := GenerateKey(random, size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to generate key")
|
t.Errorf("failed to generate key")
|
||||||
}
|
}
|
||||||
|
@ -99,6 +103,9 @@ func TestDecryptOAEP(t *testing.T) {
|
||||||
t.Errorf("#%d,%d (blind) bad result: %#v (want %#v)", i, j, out, message.in)
|
t.Errorf("#%d,%d (blind) bad result: %#v (want %#v)", i, j, out, message.in)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if testing.Short() {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,9 +93,10 @@ const (
|
||||||
|
|
||||||
// ConnectionState records basic TLS details about the connection.
|
// ConnectionState records basic TLS details about the connection.
|
||||||
type ConnectionState struct {
|
type ConnectionState struct {
|
||||||
HandshakeComplete bool
|
HandshakeComplete bool
|
||||||
CipherSuite uint16
|
CipherSuite uint16
|
||||||
NegotiatedProtocol string
|
NegotiatedProtocol string
|
||||||
|
NegotiatedProtocolIsMutual bool
|
||||||
|
|
||||||
// the certificate chain that was presented by the other side
|
// the certificate chain that was presented by the other side
|
||||||
PeerCertificates []*x509.Certificate
|
PeerCertificates []*x509.Certificate
|
||||||
|
@ -124,7 +125,6 @@ type Config struct {
|
||||||
RootCAs *CASet
|
RootCAs *CASet
|
||||||
|
|
||||||
// NextProtos is a list of supported, application level protocols.
|
// NextProtos is a list of supported, application level protocols.
|
||||||
// Currently only server-side handling is supported.
|
|
||||||
NextProtos []string
|
NextProtos []string
|
||||||
|
|
||||||
// ServerName is included in the client's handshake to support virtual
|
// ServerName is included in the client's handshake to support virtual
|
||||||
|
|
|
@ -35,7 +35,8 @@ type Conn struct {
|
||||||
ocspResponse []byte // stapled OCSP response
|
ocspResponse []byte // stapled OCSP response
|
||||||
peerCertificates []*x509.Certificate
|
peerCertificates []*x509.Certificate
|
||||||
|
|
||||||
clientProtocol string
|
clientProtocol string
|
||||||
|
clientProtocolFallback bool
|
||||||
|
|
||||||
// first permanent error
|
// first permanent error
|
||||||
errMutex sync.Mutex
|
errMutex sync.Mutex
|
||||||
|
@ -761,6 +762,7 @@ func (c *Conn) ConnectionState() ConnectionState {
|
||||||
state.HandshakeComplete = c.handshakeComplete
|
state.HandshakeComplete = c.handshakeComplete
|
||||||
if c.handshakeComplete {
|
if c.handshakeComplete {
|
||||||
state.NegotiatedProtocol = c.clientProtocol
|
state.NegotiatedProtocol = c.clientProtocol
|
||||||
|
state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback
|
||||||
state.CipherSuite = c.cipherSuite
|
state.CipherSuite = c.cipherSuite
|
||||||
state.PeerCertificates = c.peerCertificates
|
state.PeerCertificates = c.peerCertificates
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ func (c *Conn) clientHandshake() os.Error {
|
||||||
serverName: c.config.ServerName,
|
serverName: c.config.ServerName,
|
||||||
supportedCurves: []uint16{curveP256, curveP384, curveP521},
|
supportedCurves: []uint16{curveP256, curveP384, curveP521},
|
||||||
supportedPoints: []uint8{pointFormatUncompressed},
|
supportedPoints: []uint8{pointFormatUncompressed},
|
||||||
|
nextProtoNeg: len(c.config.NextProtos) > 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
t := uint32(c.config.time())
|
t := uint32(c.config.time())
|
||||||
|
@ -66,6 +67,11 @@ func (c *Conn) clientHandshake() os.Error {
|
||||||
return c.sendAlert(alertUnexpectedMessage)
|
return c.sendAlert(alertUnexpectedMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !hello.nextProtoNeg && serverHello.nextProtoNeg {
|
||||||
|
c.sendAlert(alertHandshakeFailure)
|
||||||
|
return os.ErrorString("server advertised unrequested NPN")
|
||||||
|
}
|
||||||
|
|
||||||
suite, suiteId := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite)
|
suite, suiteId := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite)
|
||||||
if suite == nil {
|
if suite == nil {
|
||||||
return c.sendAlert(alertHandshakeFailure)
|
return c.sendAlert(alertHandshakeFailure)
|
||||||
|
@ -267,6 +273,17 @@ func (c *Conn) clientHandshake() os.Error {
|
||||||
c.out.prepareCipherSpec(clientCipher, clientHash)
|
c.out.prepareCipherSpec(clientCipher, clientHash)
|
||||||
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
|
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
|
||||||
|
|
||||||
|
if serverHello.nextProtoNeg {
|
||||||
|
nextProto := new(nextProtoMsg)
|
||||||
|
proto, fallback := mutualProtocol(c.config.NextProtos, serverHello.nextProtos)
|
||||||
|
nextProto.proto = proto
|
||||||
|
c.clientProtocol = proto
|
||||||
|
c.clientProtocolFallback = fallback
|
||||||
|
|
||||||
|
finishedHash.Write(nextProto.marshal())
|
||||||
|
c.writeRecord(recordTypeHandshake, nextProto.marshal())
|
||||||
|
}
|
||||||
|
|
||||||
finished := new(finishedMsg)
|
finished := new(finishedMsg)
|
||||||
finished.verifyData = finishedHash.clientSum(masterSecret)
|
finished.verifyData = finishedHash.clientSum(masterSecret)
|
||||||
finishedHash.Write(finished.marshal())
|
finishedHash.Write(finished.marshal())
|
||||||
|
@ -299,3 +316,19 @@ func (c *Conn) clientHandshake() os.Error {
|
||||||
c.cipherSuite = suiteId
|
c.cipherSuite = suiteId
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mutualProtocol finds the mutual Next Protocol Negotiation protocol given the
|
||||||
|
// set of client and server supported protocols. The set of client supported
|
||||||
|
// protocols must not be empty. It returns the resulting protocol and flag
|
||||||
|
// indicating if the fallback case was reached.
|
||||||
|
func mutualProtocol(clientProtos, serverProtos []string) (string, bool) {
|
||||||
|
for _, s := range serverProtos {
|
||||||
|
for _, c := range clientProtos {
|
||||||
|
if s == c {
|
||||||
|
return s, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientProtos[0], true
|
||||||
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ func TestRunClient(t *testing.T) {
|
||||||
|
|
||||||
testConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}
|
testConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}
|
||||||
|
|
||||||
conn, err := Dial("tcp", "", "127.0.0.1:10443", testConfig)
|
conn, err := Dial("tcp", "127.0.0.1:10443", testConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,11 @@ func TestMarshalUnmarshal(t *testing.T) {
|
||||||
for i, iface := range tests {
|
for i, iface := range tests {
|
||||||
ty := reflect.NewValue(iface).Type()
|
ty := reflect.NewValue(iface).Type()
|
||||||
|
|
||||||
for j := 0; j < 100; j++ {
|
n := 100
|
||||||
|
if testing.Short() {
|
||||||
|
n = 5
|
||||||
|
}
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
v, ok := quick.Value(ty, rand)
|
v, ok := quick.Value(ty, rand)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("#%d: failed to create value", i)
|
t.Errorf("#%d: failed to create value", i)
|
||||||
|
|
|
@ -87,8 +87,9 @@ func Listen(network, laddr string, config *Config) (*Listener, os.Error) {
|
||||||
// Dial interprets a nil configuration as equivalent to
|
// Dial interprets a nil configuration as equivalent to
|
||||||
// the zero configuration; see the documentation of Config
|
// the zero configuration; see the documentation of Config
|
||||||
// for the defaults.
|
// for the defaults.
|
||||||
func Dial(network, laddr, raddr string, config *Config) (*Conn, os.Error) {
|
func Dial(network, addr string, config *Config) (*Conn, os.Error) {
|
||||||
c, err := net.Dial(network, laddr, raddr)
|
raddr := addr
|
||||||
|
c, err := net.Dial(network, raddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -304,6 +304,42 @@ const (
|
||||||
KeyUsageDecipherOnly
|
KeyUsageDecipherOnly
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RFC 5280, 4.2.1.12 Extended Key Usage
|
||||||
|
//
|
||||||
|
// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 }
|
||||||
|
//
|
||||||
|
// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
|
||||||
|
//
|
||||||
|
// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 }
|
||||||
|
// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 }
|
||||||
|
// id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 }
|
||||||
|
// id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 }
|
||||||
|
// id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 }
|
||||||
|
// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 }
|
||||||
|
var (
|
||||||
|
oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0}
|
||||||
|
oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1}
|
||||||
|
oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2}
|
||||||
|
oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3}
|
||||||
|
oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4}
|
||||||
|
oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8}
|
||||||
|
oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9}
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExtKeyUsage represents an extended set of actions that are valid for a given key.
|
||||||
|
// Each of the ExtKeyUsage* constants define a unique action.
|
||||||
|
type ExtKeyUsage int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ExtKeyUsageAny ExtKeyUsage = iota
|
||||||
|
ExtKeyUsageServerAuth
|
||||||
|
ExtKeyUsageClientAuth
|
||||||
|
ExtKeyUsageCodeSigning
|
||||||
|
ExtKeyUsageEmailProtection
|
||||||
|
ExtKeyUsageTimeStamping
|
||||||
|
ExtKeyUsageOCSPSigning
|
||||||
|
)
|
||||||
|
|
||||||
// A Certificate represents an X.509 certificate.
|
// A Certificate represents an X.509 certificate.
|
||||||
type Certificate struct {
|
type Certificate struct {
|
||||||
Raw []byte // Raw ASN.1 DER contents.
|
Raw []byte // Raw ASN.1 DER contents.
|
||||||
|
@ -320,6 +356,9 @@ type Certificate struct {
|
||||||
NotBefore, NotAfter *time.Time // Validity bounds.
|
NotBefore, NotAfter *time.Time // Validity bounds.
|
||||||
KeyUsage KeyUsage
|
KeyUsage KeyUsage
|
||||||
|
|
||||||
|
ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages.
|
||||||
|
UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package.
|
||||||
|
|
||||||
BasicConstraintsValid bool // if true then the next two fields are valid.
|
BasicConstraintsValid bool // if true then the next two fields are valid.
|
||||||
IsCA bool
|
IsCA bool
|
||||||
MaxPathLen int
|
MaxPathLen int
|
||||||
|
@ -666,6 +705,44 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) {
|
||||||
out.AuthorityKeyId = a.Id
|
out.AuthorityKeyId = a.Id
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
case 37:
|
||||||
|
// RFC 5280, 4.2.1.12. Extended Key Usage
|
||||||
|
|
||||||
|
// id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
|
||||||
|
//
|
||||||
|
// ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
|
||||||
|
//
|
||||||
|
// KeyPurposeId ::= OBJECT IDENTIFIER
|
||||||
|
|
||||||
|
var keyUsage []asn1.ObjectIdentifier
|
||||||
|
_, err = asn1.Unmarshal(e.Value, &keyUsage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, u := range keyUsage {
|
||||||
|
switch {
|
||||||
|
case u.Equal(oidExtKeyUsageAny):
|
||||||
|
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageAny)
|
||||||
|
case u.Equal(oidExtKeyUsageServerAuth):
|
||||||
|
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageServerAuth)
|
||||||
|
case u.Equal(oidExtKeyUsageClientAuth):
|
||||||
|
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageClientAuth)
|
||||||
|
case u.Equal(oidExtKeyUsageCodeSigning):
|
||||||
|
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageCodeSigning)
|
||||||
|
case u.Equal(oidExtKeyUsageEmailProtection):
|
||||||
|
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageEmailProtection)
|
||||||
|
case u.Equal(oidExtKeyUsageTimeStamping):
|
||||||
|
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageTimeStamping)
|
||||||
|
case u.Equal(oidExtKeyUsageOCSPSigning):
|
||||||
|
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageOCSPSigning)
|
||||||
|
default:
|
||||||
|
out.UnknownExtKeyUsage = append(out.UnknownExtKeyUsage, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
case 14:
|
case 14:
|
||||||
// RFC 5280, 4.2.1.2
|
// RFC 5280, 4.2.1.2
|
||||||
var keyid []byte
|
var keyid []byte
|
||||||
|
|
|
@ -143,9 +143,6 @@ func TestLineAline(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// gotest: if [ "$(uname)-$(uname -m)" = Linux-x86_64 -a "$GOARCH" = amd64 ]; then
|
|
||||||
// gotest: mkdir -p _test && $AS pclinetest.s && $LD -E main -o _test/pclinetest pclinetest.$O
|
|
||||||
// gotest: fi
|
|
||||||
func TestPCLine(t *testing.T) {
|
func TestPCLine(t *testing.T) {
|
||||||
if !dotest() {
|
if !dotest() {
|
||||||
return
|
return
|
||||||
|
|
|
@ -15,31 +15,26 @@ var fset = token.NewFileSet()
|
||||||
|
|
||||||
|
|
||||||
var grammars = []string{
|
var grammars = []string{
|
||||||
`Program = .
|
`Program = .`,
|
||||||
`,
|
|
||||||
|
|
||||||
`Program = foo .
|
`Program = foo .
|
||||||
foo = "foo" .
|
foo = "foo" .`,
|
||||||
`,
|
|
||||||
|
|
||||||
`Program = "a" | "b" "c" .
|
`Program = "a" | "b" "c" .`,
|
||||||
`,
|
|
||||||
|
|
||||||
`Program = "a" ... "z" .
|
`Program = "a" ... "z" .`,
|
||||||
`,
|
|
||||||
|
|
||||||
`Program = Song .
|
`Program = Song .
|
||||||
Song = { Note } .
|
Song = { Note } .
|
||||||
Note = Do | (Re | Mi | Fa | So | La) | Ti .
|
Note = Do | (Re | Mi | Fa | So | La) | Ti .
|
||||||
Do = "c" .
|
Do = "c" .
|
||||||
Re = "d" .
|
Re = "d" .
|
||||||
Mi = "e" .
|
Mi = "e" .
|
||||||
Fa = "f" .
|
Fa = "f" .
|
||||||
So = "g" .
|
So = "g" .
|
||||||
La = "a" .
|
La = "a" .
|
||||||
Ti = ti .
|
Ti = ti .
|
||||||
ti = "b" .
|
ti = "b" .`,
|
||||||
`,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ type parser struct {
|
||||||
scanner scanner.Scanner
|
scanner scanner.Scanner
|
||||||
pos token.Pos // token position
|
pos token.Pos // token position
|
||||||
tok token.Token // one token look-ahead
|
tok token.Token // one token look-ahead
|
||||||
lit []byte // token literal
|
lit string // token literal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ func (p *parser) errorExpected(pos token.Pos, msg string) {
|
||||||
// make the error message more specific
|
// make the error message more specific
|
||||||
msg += ", found '" + p.tok.String() + "'"
|
msg += ", found '" + p.tok.String() + "'"
|
||||||
if p.tok.IsLiteral() {
|
if p.tok.IsLiteral() {
|
||||||
msg += " " + string(p.lit)
|
msg += " " + p.lit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.error(pos, msg)
|
p.error(pos, msg)
|
||||||
|
@ -63,7 +63,7 @@ func (p *parser) expect(tok token.Token) token.Pos {
|
||||||
|
|
||||||
func (p *parser) parseIdentifier() *Name {
|
func (p *parser) parseIdentifier() *Name {
|
||||||
pos := p.pos
|
pos := p.pos
|
||||||
name := string(p.lit)
|
name := p.lit
|
||||||
p.expect(token.IDENT)
|
p.expect(token.IDENT)
|
||||||
return &Name{pos, name}
|
return &Name{pos, name}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ func (p *parser) parseToken() *Token {
|
||||||
pos := p.pos
|
pos := p.pos
|
||||||
value := ""
|
value := ""
|
||||||
if p.tok == token.STRING {
|
if p.tok == token.STRING {
|
||||||
value, _ = strconv.Unquote(string(p.lit))
|
value, _ = strconv.Unquote(p.lit)
|
||||||
// Unquote may fail with an error, but only if the scanner found
|
// Unquote may fail with an error, but only if the scanner found
|
||||||
// an illegal string in the first place. In this case the error
|
// an illegal string in the first place. In this case the error
|
||||||
// has already been reported.
|
// has already been reported.
|
||||||
|
|
|
@ -22,7 +22,7 @@ type parser struct {
|
||||||
file *token.File
|
file *token.File
|
||||||
pos token.Pos // token position
|
pos token.Pos // token position
|
||||||
tok token.Token // one token look-ahead
|
tok token.Token // one token look-ahead
|
||||||
lit []byte // token literal
|
lit string // token literal
|
||||||
|
|
||||||
packs map[string]string // PackageName -> ImportPath
|
packs map[string]string // PackageName -> ImportPath
|
||||||
rules map[string]expr // RuleName -> Expression
|
rules map[string]expr // RuleName -> Expression
|
||||||
|
@ -62,7 +62,7 @@ func (p *parser) errorExpected(pos token.Pos, msg string) {
|
||||||
// make the error message more specific
|
// make the error message more specific
|
||||||
msg += ", found '" + p.tok.String() + "'"
|
msg += ", found '" + p.tok.String() + "'"
|
||||||
if p.tok.IsLiteral() {
|
if p.tok.IsLiteral() {
|
||||||
msg += " " + string(p.lit)
|
msg += " " + p.lit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.error(pos, msg)
|
p.error(pos, msg)
|
||||||
|
@ -80,7 +80,7 @@ func (p *parser) expect(tok token.Token) token.Pos {
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseIdentifier() string {
|
func (p *parser) parseIdentifier() string {
|
||||||
name := string(p.lit)
|
name := p.lit
|
||||||
p.expect(token.IDENT)
|
p.expect(token.IDENT)
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ func (p *parser) parseRuleName() (string, bool) {
|
||||||
func (p *parser) parseString() string {
|
func (p *parser) parseString() string {
|
||||||
s := ""
|
s := ""
|
||||||
if p.tok == token.STRING {
|
if p.tok == token.STRING {
|
||||||
s, _ = strconv.Unquote(string(p.lit))
|
s, _ = strconv.Unquote(p.lit)
|
||||||
// Unquote may fail with an error, but only if the scanner found
|
// Unquote may fail with an error, but only if the scanner found
|
||||||
// an illegal string in the first place. In this case the error
|
// an illegal string in the first place. In this case the error
|
||||||
// has already been reported.
|
// has already been reported.
|
||||||
|
@ -181,7 +181,7 @@ func (p *parser) parseField() expr {
|
||||||
var fname string
|
var fname string
|
||||||
switch p.tok {
|
switch p.tok {
|
||||||
case token.ILLEGAL:
|
case token.ILLEGAL:
|
||||||
if string(p.lit) != "@" {
|
if p.lit != "@" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fname = "@"
|
fname = "@"
|
||||||
|
|
|
@ -286,11 +286,11 @@ func connect(display string) (conn net.Conn, displayStr string, err os.Error) {
|
||||||
}
|
}
|
||||||
// Make the connection.
|
// Make the connection.
|
||||||
if socket != "" {
|
if socket != "" {
|
||||||
conn, err = net.Dial("unix", "", socket+":"+displayStr)
|
conn, err = net.Dial("unix", socket+":"+displayStr)
|
||||||
} else if host != "" {
|
} else if host != "" {
|
||||||
conn, err = net.Dial(protocol, "", host+":"+strconv.Itoa(6000+displayInt))
|
conn, err = net.Dial(protocol, host+":"+strconv.Itoa(6000+displayInt))
|
||||||
} else {
|
} else {
|
||||||
conn, err = net.Dial("unix", "", "/tmp/.X11-unix/X"+displayStr)
|
conn, err = net.Dial("unix", "/tmp/.X11-unix/X"+displayStr)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", os.NewError("cannot connect to " + display + ": " + err.String())
|
return nil, "", os.NewError("cannot connect to " + display + ": " + err.String())
|
||||||
|
|
|
@ -39,9 +39,13 @@ type job struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTests(t *testing.T, baseName string, tests []test) {
|
func runTests(t *testing.T, baseName string, tests []test) {
|
||||||
for i, test := range tests {
|
delta := 1
|
||||||
|
if testing.Short() {
|
||||||
|
delta = 16
|
||||||
|
}
|
||||||
|
for i := 0; i < len(tests); i += delta {
|
||||||
name := fmt.Sprintf("%s[%d]", baseName, i)
|
name := fmt.Sprintf("%s[%d]", baseName, i)
|
||||||
test.run(t, name)
|
tests[i].run(t, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -205,7 +205,7 @@ func parseLoad(args []byte) (ident string, path string, err os.Error) {
|
||||||
sc, ev := newScanner(args)
|
sc, ev := newScanner(args)
|
||||||
|
|
||||||
var toks [4]token.Token
|
var toks [4]token.Token
|
||||||
var lits [4][]byte
|
var lits [4]string
|
||||||
for i := range toks {
|
for i := range toks {
|
||||||
_, toks[i], lits[i] = sc.Scan()
|
_, toks[i], lits[i] = sc.Scan()
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
|
|
||||||
flag.Bool(...) // global options
|
flag.Bool(...) // global options
|
||||||
flag.Parse() // parse leading command
|
flag.Parse() // parse leading command
|
||||||
subcmd := flag.Arg[0]
|
subcmd := flag.Arg(0)
|
||||||
switch subcmd {
|
switch subcmd {
|
||||||
// add per-subcommand options
|
// add per-subcommand options
|
||||||
}
|
}
|
||||||
|
|
|
@ -442,6 +442,9 @@ func BenchmarkSprintfPrefixedInt(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCountMallocs(t *testing.T) {
|
func TestCountMallocs(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
mallocs := 0 - runtime.MemStats.Mallocs
|
mallocs := 0 - runtime.MemStats.Mallocs
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
Sprintf("")
|
Sprintf("")
|
||||||
|
|
|
@ -66,7 +66,7 @@ type Decl interface {
|
||||||
// A Comment node represents a single //-style or /*-style comment.
|
// A Comment node represents a single //-style or /*-style comment.
|
||||||
type Comment struct {
|
type Comment struct {
|
||||||
Slash token.Pos // position of "/" starting the comment
|
Slash token.Pos // position of "/" starting the comment
|
||||||
Text []byte // comment text (excluding '\n' for //-style comments)
|
Text string // comment text (excluding '\n' for //-style comments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -199,7 +199,7 @@ type (
|
||||||
BasicLit struct {
|
BasicLit struct {
|
||||||
ValuePos token.Pos // literal position
|
ValuePos token.Pos // literal position
|
||||||
Kind token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING
|
Kind token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING
|
||||||
Value []byte // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
|
Value string // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
|
||||||
}
|
}
|
||||||
|
|
||||||
// A FuncLit node represents a function literal.
|
// A FuncLit node represents a function literal.
|
||||||
|
@ -781,7 +781,7 @@ type (
|
||||||
ImportSpec struct {
|
ImportSpec struct {
|
||||||
Doc *CommentGroup // associated documentation; or nil
|
Doc *CommentGroup // associated documentation; or nil
|
||||||
Name *Ident // local package name (including "."); or nil
|
Name *Ident // local package name (including "."); or nil
|
||||||
Path *BasicLit // package path
|
Path *BasicLit // import path
|
||||||
Comment *CommentGroup // line comments; or nil
|
Comment *CommentGroup // line comments; or nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -925,8 +925,9 @@ type File struct {
|
||||||
Package token.Pos // position of "package" keyword
|
Package token.Pos // position of "package" keyword
|
||||||
Name *Ident // package name
|
Name *Ident // package name
|
||||||
Decls []Decl // top-level declarations; or nil
|
Decls []Decl // top-level declarations; or nil
|
||||||
Scope *Scope // package scope
|
Scope *Scope // package scope (this file only)
|
||||||
Unresolved []*Ident // unresolved global identifiers
|
Imports []*ImportSpec // imports in this file
|
||||||
|
Unresolved []*Ident // unresolved identifiers in this file
|
||||||
Comments []*CommentGroup // list of all comments in the source file
|
Comments []*CommentGroup // list of all comments in the source file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -944,9 +945,10 @@ func (f *File) End() token.Pos {
|
||||||
// collectively building a Go package.
|
// collectively building a Go package.
|
||||||
//
|
//
|
||||||
type Package struct {
|
type Package struct {
|
||||||
Name string // package name
|
Name string // package name
|
||||||
Scope *Scope // package scope
|
Scope *Scope // package scope
|
||||||
Files map[string]*File // Go source files by filename
|
Imports map[string]*Scope // map of import path -> package scope across all files
|
||||||
|
Files map[string]*File // Go source files by filename
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -304,7 +304,7 @@ const (
|
||||||
// separator is an empty //-style comment that is interspersed between
|
// separator is an empty //-style comment that is interspersed between
|
||||||
// different comment groups when they are concatenated into a single group
|
// different comment groups when they are concatenated into a single group
|
||||||
//
|
//
|
||||||
var separator = &Comment{noPos, []byte("//")}
|
var separator = &Comment{noPos, "//"}
|
||||||
|
|
||||||
|
|
||||||
// MergePackageFiles creates a file AST by merging the ASTs of the
|
// MergePackageFiles creates a file AST by merging the ASTs of the
|
||||||
|
@ -426,5 +426,6 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(gri) need to compute pkgScope and unresolved identifiers!
|
// TODO(gri) need to compute pkgScope and unresolved identifiers!
|
||||||
return &File{doc, pos, NewIdent(pkg.Name), decls, nil, nil, comments}
|
// TODO(gri) need to compute imports!
|
||||||
|
return &File{doc, pos, NewIdent(pkg.Name), decls, nil, nil, nil, comments}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements NewPackage.
|
||||||
|
|
||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/scanner"
|
||||||
|
"go/token"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type pkgBuilder struct {
|
||||||
|
scanner.ErrorVector
|
||||||
|
fset *token.FileSet
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *pkgBuilder) error(pos token.Pos, msg string) {
|
||||||
|
p.Error(p.fset.Position(pos), msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *pkgBuilder) errorf(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
p.error(pos, fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *pkgBuilder) declare(scope, altScope *Scope, obj *Object) {
|
||||||
|
alt := scope.Insert(obj)
|
||||||
|
if alt == nil && altScope != nil {
|
||||||
|
// see if there is a conflicting declaration in altScope
|
||||||
|
alt = altScope.Lookup(obj.Name)
|
||||||
|
}
|
||||||
|
if alt != nil {
|
||||||
|
prevDecl := ""
|
||||||
|
if pos := alt.Pos(); pos.IsValid() {
|
||||||
|
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.fset.Position(pos))
|
||||||
|
}
|
||||||
|
p.error(obj.Pos(), fmt.Sprintf("%s redeclared in this block%s", obj.Name, prevDecl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func resolve(scope *Scope, ident *Ident) bool {
|
||||||
|
for ; scope != nil; scope = scope.Outer {
|
||||||
|
if obj := scope.Lookup(ident.Name); obj != nil {
|
||||||
|
ident.Obj = obj
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NewPackage uses an Importer to resolve imports. Given an importPath,
|
||||||
|
// an importer returns the imported package's name, its scope of exported
|
||||||
|
// objects, and an error, if any.
|
||||||
|
//
|
||||||
|
type Importer func(path string) (name string, scope *Scope, err os.Error)
|
||||||
|
|
||||||
|
|
||||||
|
// NewPackage creates a new Package node from a set of File nodes. It resolves
|
||||||
|
// unresolved identifiers across files and updates each file's Unresolved list
|
||||||
|
// accordingly. If a non-nil importer and universe scope are provided, they are
|
||||||
|
// used to resolve identifiers not declared in any of the package files. Any
|
||||||
|
// remaining unresolved identifiers are reported as undeclared. If the files
|
||||||
|
// belong to different packages, one package name is selected and files with
|
||||||
|
// different package name are reported and then ignored.
|
||||||
|
// The result is a package node and a scanner.ErrorList if there were errors.
|
||||||
|
//
|
||||||
|
func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer, universe *Scope) (*Package, os.Error) {
|
||||||
|
var p pkgBuilder
|
||||||
|
p.fset = fset
|
||||||
|
|
||||||
|
// complete package scope
|
||||||
|
pkgName := ""
|
||||||
|
pkgScope := NewScope(universe)
|
||||||
|
for _, file := range files {
|
||||||
|
// package names must match
|
||||||
|
switch name := file.Name.Name; {
|
||||||
|
case pkgName == "":
|
||||||
|
pkgName = name
|
||||||
|
case name != pkgName:
|
||||||
|
p.errorf(file.Package, "package %s; expected %s", name, pkgName)
|
||||||
|
continue // ignore this file
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect top-level file objects in package scope
|
||||||
|
for _, obj := range file.Scope.Objects {
|
||||||
|
p.declare(pkgScope, nil, obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// imports maps import paths to package names and scopes
|
||||||
|
// TODO(gri): Eventually we like to get to the import scope from
|
||||||
|
// a package object. Then we can have a map path -> Obj.
|
||||||
|
type importedPkg struct {
|
||||||
|
name string
|
||||||
|
scope *Scope
|
||||||
|
}
|
||||||
|
imports := make(map[string]*importedPkg)
|
||||||
|
|
||||||
|
// complete file scopes with imports and resolve identifiers
|
||||||
|
for _, file := range files {
|
||||||
|
// ignore file if it belongs to a different package
|
||||||
|
// (error has already been reported)
|
||||||
|
if file.Name.Name != pkgName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// build file scope by processing all imports
|
||||||
|
importErrors := false
|
||||||
|
fileScope := NewScope(pkgScope)
|
||||||
|
for _, spec := range file.Imports {
|
||||||
|
// add import to global map of imports
|
||||||
|
path := string(spec.Path.Value)
|
||||||
|
path = path[1 : len(path)-1] // strip ""'s
|
||||||
|
pkg := imports[path]
|
||||||
|
if pkg == nil {
|
||||||
|
if importer == nil {
|
||||||
|
importErrors = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, scope, err := importer(path)
|
||||||
|
if err != nil {
|
||||||
|
p.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err)
|
||||||
|
importErrors = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pkg = &importedPkg{name, scope}
|
||||||
|
imports[path] = pkg
|
||||||
|
// TODO(gri) If a local package name != "." is provided,
|
||||||
|
// global identifier resolution could proceed even if the
|
||||||
|
// import failed. Consider adjusting the logic here a bit.
|
||||||
|
}
|
||||||
|
// local name overrides imported package name
|
||||||
|
name := pkg.name
|
||||||
|
if spec.Name != nil {
|
||||||
|
name = spec.Name.Name
|
||||||
|
}
|
||||||
|
// add import to file scope
|
||||||
|
if name == "." {
|
||||||
|
// merge imported scope with file scope
|
||||||
|
for _, obj := range pkg.scope.Objects {
|
||||||
|
p.declare(fileScope, pkgScope, obj)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// declare imported package object in file scope
|
||||||
|
obj := NewObj(Pkg, name)
|
||||||
|
obj.Decl = spec
|
||||||
|
p.declare(fileScope, pkgScope, obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve identifiers
|
||||||
|
if importErrors {
|
||||||
|
// don't use the universe scope without correct imports
|
||||||
|
// (objects in the universe may be shadowed by imports;
|
||||||
|
// with missing imports identifiers might get resolved
|
||||||
|
// wrongly)
|
||||||
|
pkgScope.Outer = nil
|
||||||
|
}
|
||||||
|
i := 0
|
||||||
|
for _, ident := range file.Unresolved {
|
||||||
|
if !resolve(fileScope, ident) {
|
||||||
|
p.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
|
||||||
|
file.Unresolved[i] = ident
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
file.Unresolved = file.Unresolved[0:i]
|
||||||
|
pkgScope.Outer = universe // reset universe scope
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect all import paths and respective package scopes
|
||||||
|
importedScopes := make(map[string]*Scope)
|
||||||
|
for path, pkg := range imports {
|
||||||
|
importedScopes[path] = pkg.scope
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Package{pkgName, pkgScope, importedScopes, files}, p.GetError(scanner.Sorted)
|
||||||
|
}
|
|
@ -39,16 +39,14 @@ func (s *Scope) Lookup(name string) *Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Insert attempts to insert a named object into the scope s.
|
// Insert attempts to insert a named object obj into the scope s.
|
||||||
// If the scope does not contain an object with that name yet,
|
// If the scope already contains an object alt with the same name,
|
||||||
// Insert inserts the object and returns it. Otherwise, Insert
|
// Insert leaves the scope unchanged and returns alt. Otherwise
|
||||||
// leaves the scope unchanged and returns the object found in
|
// it inserts obj and returns nil."
|
||||||
// the scope instead.
|
|
||||||
//
|
//
|
||||||
func (s *Scope) Insert(obj *Object) (alt *Object) {
|
func (s *Scope) Insert(obj *Object) (alt *Object) {
|
||||||
if alt = s.Objects[obj.Name]; alt == nil {
|
if alt = s.Objects[obj.Name]; alt == nil {
|
||||||
s.Objects[obj.Name] = obj
|
s.Objects[obj.Name] = obj
|
||||||
alt = obj
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -101,6 +99,11 @@ func (obj *Object) Pos() token.Pos {
|
||||||
return n.Pos()
|
return n.Pos()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case *ImportSpec:
|
||||||
|
if d.Name != nil && d.Name.Name == name {
|
||||||
|
return d.Name.Pos()
|
||||||
|
}
|
||||||
|
return d.Path.Pos()
|
||||||
case *ValueSpec:
|
case *ValueSpec:
|
||||||
for _, n := range d.Names {
|
for _, n := range d.Names {
|
||||||
if n.Name == name {
|
if n.Name == name {
|
||||||
|
|
|
@ -286,7 +286,7 @@ func unindent(block [][]byte) {
|
||||||
// nor to have trailing spaces at the end of lines.
|
// nor to have trailing spaces at the end of lines.
|
||||||
// The comment markers have already been removed.
|
// The comment markers have already been removed.
|
||||||
//
|
//
|
||||||
// Turn each run of multiple \n into </p><p>
|
// Turn each run of multiple \n into </p><p>.
|
||||||
// Turn each run of indented lines into a <pre> block without indent.
|
// Turn each run of indented lines into a <pre> block without indent.
|
||||||
//
|
//
|
||||||
// URLs in the comment text are converted into links; if the URL also appears
|
// URLs in the comment text are converted into links; if the URL also appears
|
||||||
|
|
|
@ -66,7 +66,7 @@ func (doc *docReader) addDoc(comments *ast.CommentGroup) {
|
||||||
n2 := len(comments.List)
|
n2 := len(comments.List)
|
||||||
list := make([]*ast.Comment, n1+1+n2) // + 1 for separator line
|
list := make([]*ast.Comment, n1+1+n2) // + 1 for separator line
|
||||||
copy(list, doc.doc.List)
|
copy(list, doc.doc.List)
|
||||||
list[n1] = &ast.Comment{token.NoPos, []byte("//")} // separator line
|
list[n1] = &ast.Comment{token.NoPos, "//"} // separator line
|
||||||
copy(list[n1+1:], comments.List)
|
copy(list[n1+1:], comments.List)
|
||||||
doc.doc = &ast.CommentGroup{list}
|
doc.doc = &ast.CommentGroup{list}
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,7 @@ func baseTypeName(typ ast.Expr) string {
|
||||||
// if the type is not exported, the effect to
|
// if the type is not exported, the effect to
|
||||||
// a client is as if there were no type name
|
// a client is as if there were no type name
|
||||||
if t.IsExported() {
|
if t.IsExported() {
|
||||||
return string(t.Name)
|
return t.Name
|
||||||
}
|
}
|
||||||
case *ast.StarExpr:
|
case *ast.StarExpr:
|
||||||
return baseTypeName(t.X)
|
return baseTypeName(t.X)
|
||||||
|
@ -300,9 +300,9 @@ func (doc *docReader) addFile(src *ast.File) {
|
||||||
// collect BUG(...) comments
|
// collect BUG(...) comments
|
||||||
for _, c := range src.Comments {
|
for _, c := range src.Comments {
|
||||||
text := c.List[0].Text
|
text := c.List[0].Text
|
||||||
if m := bug_markers.FindIndex(text); m != nil {
|
if m := bug_markers.FindStringIndex(text); m != nil {
|
||||||
// found a BUG comment; maybe empty
|
// found a BUG comment; maybe empty
|
||||||
if btxt := text[m[1]:]; bug_content.Match(btxt) {
|
if btxt := text[m[1]:]; bug_content.MatchString(btxt) {
|
||||||
// non-empty BUG comment; collect comment without BUG prefix
|
// non-empty BUG comment; collect comment without BUG prefix
|
||||||
list := copyCommentList(c.List)
|
list := copyCommentList(c.List)
|
||||||
list[0].Text = text[m[1]:]
|
list[0].Text = text[m[1]:]
|
||||||
|
|
|
@ -69,7 +69,7 @@ func ParseExpr(fset *token.FileSet, filename string, src interface{}) (ast.Expr,
|
||||||
|
|
||||||
var p parser
|
var p parser
|
||||||
p.init(fset, filename, data, 0)
|
p.init(fset, filename, data, 0)
|
||||||
x := p.parseExpr()
|
x := p.parseRhs()
|
||||||
if p.tok == token.SEMICOLON {
|
if p.tok == token.SEMICOLON {
|
||||||
p.next() // consume automatically inserted semicolon, if any
|
p.next() // consume automatically inserted semicolon, if any
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,8 @@ func ParseFiles(fset *token.FileSet, filenames []string, mode uint) (pkgs map[st
|
||||||
name := src.Name.Name
|
name := src.Name.Name
|
||||||
pkg, found := pkgs[name]
|
pkg, found := pkgs[name]
|
||||||
if !found {
|
if !found {
|
||||||
pkg = &ast.Package{name, nil, make(map[string]*ast.File)}
|
// TODO(gri) Use NewPackage here; reconsider ParseFiles API.
|
||||||
|
pkg = &ast.Package{name, nil, nil, make(map[string]*ast.File)}
|
||||||
pkgs[name] = pkg
|
pkgs[name] = pkg
|
||||||
}
|
}
|
||||||
pkg.Files[filename] = src
|
pkg.Files[filename] = src
|
||||||
|
|
|
@ -47,17 +47,18 @@ type parser struct {
|
||||||
lineComment *ast.CommentGroup // last line comment
|
lineComment *ast.CommentGroup // last line comment
|
||||||
|
|
||||||
// Next token
|
// Next token
|
||||||
pos token.Pos // token position
|
pos token.Pos // token position
|
||||||
tok token.Token // one token look-ahead
|
tok token.Token // one token look-ahead
|
||||||
lit_ []byte // token literal (slice into original source, don't hold on to it)
|
lit string // token literal
|
||||||
|
|
||||||
// Non-syntactic parser control
|
// Non-syntactic parser control
|
||||||
exprLev int // < 0: in control clause, >= 0: in expression
|
exprLev int // < 0: in control clause, >= 0: in expression
|
||||||
|
|
||||||
// Ordinary identifer scopes
|
// Ordinary identifer scopes
|
||||||
pkgScope *ast.Scope // pkgScope.Outer == nil
|
pkgScope *ast.Scope // pkgScope.Outer == nil
|
||||||
topScope *ast.Scope // top-most scope; may be pkgScope
|
topScope *ast.Scope // top-most scope; may be pkgScope
|
||||||
unresolved []*ast.Ident // unresolved global identifiers
|
unresolved []*ast.Ident // unresolved identifiers
|
||||||
|
imports []*ast.ImportSpec // list of imports
|
||||||
|
|
||||||
// Label scope
|
// Label scope
|
||||||
// (maintained by open/close LabelScope)
|
// (maintained by open/close LabelScope)
|
||||||
|
@ -95,15 +96,6 @@ func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode uin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) lit() []byte {
|
|
||||||
// make a copy of p.lit_ so that we don't hold on to
|
|
||||||
// a copy of the entire source indirectly in the AST
|
|
||||||
t := make([]byte, len(p.lit_))
|
|
||||||
copy(t, p.lit_)
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Scoping support
|
// Scoping support
|
||||||
|
|
||||||
|
@ -141,13 +133,13 @@ func (p *parser) closeLabelScope() {
|
||||||
|
|
||||||
func (p *parser) declare(decl interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
|
func (p *parser) declare(decl interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
|
||||||
for _, ident := range idents {
|
for _, ident := range idents {
|
||||||
|
assert(ident.Obj == nil, "identifier already declared or resolved")
|
||||||
if ident.Name != "_" {
|
if ident.Name != "_" {
|
||||||
obj := ast.NewObj(kind, ident.Name)
|
obj := ast.NewObj(kind, ident.Name)
|
||||||
// remember the corresponding declaration for redeclaration
|
// remember the corresponding declaration for redeclaration
|
||||||
// errors and global variable resolution/typechecking phase
|
// errors and global variable resolution/typechecking phase
|
||||||
obj.Decl = decl
|
obj.Decl = decl
|
||||||
alt := scope.Insert(obj)
|
if alt := scope.Insert(obj); alt != nil && p.mode&DeclarationErrors != 0 {
|
||||||
if alt != obj && p.mode&DeclarationErrors != 0 {
|
|
||||||
prevDecl := ""
|
prevDecl := ""
|
||||||
if pos := alt.Pos(); pos.IsValid() {
|
if pos := alt.Pos(); pos.IsValid() {
|
||||||
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.file.Position(pos))
|
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.file.Position(pos))
|
||||||
|
@ -166,14 +158,16 @@ func (p *parser) shortVarDecl(idents []*ast.Ident) {
|
||||||
// the same type, and at least one of the non-blank variables is new.
|
// the same type, and at least one of the non-blank variables is new.
|
||||||
n := 0 // number of new variables
|
n := 0 // number of new variables
|
||||||
for _, ident := range idents {
|
for _, ident := range idents {
|
||||||
|
assert(ident.Obj == nil, "identifier already declared or resolved")
|
||||||
if ident.Name != "_" {
|
if ident.Name != "_" {
|
||||||
obj := ast.NewObj(ast.Var, ident.Name)
|
obj := ast.NewObj(ast.Var, ident.Name)
|
||||||
// short var declarations cannot have redeclaration errors
|
// short var declarations cannot have redeclaration errors
|
||||||
// and are not global => no need to remember the respective
|
// and are not global => no need to remember the respective
|
||||||
// declaration
|
// declaration
|
||||||
alt := p.topScope.Insert(obj)
|
alt := p.topScope.Insert(obj)
|
||||||
if alt == obj {
|
if alt == nil {
|
||||||
n++ // new declaration
|
n++ // new declaration
|
||||||
|
alt = obj
|
||||||
}
|
}
|
||||||
ident.Obj = alt
|
ident.Obj = alt
|
||||||
}
|
}
|
||||||
|
@ -184,7 +178,19 @@ func (p *parser) shortVarDecl(idents []*ast.Ident) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) resolve(ident *ast.Ident) {
|
// The unresolved object is a sentinel to mark identifiers that have been added
|
||||||
|
// to the list of unresolved identifiers. The sentinel is only used for verifying
|
||||||
|
// internal consistency.
|
||||||
|
var unresolved = new(ast.Object)
|
||||||
|
|
||||||
|
|
||||||
|
func (p *parser) resolve(x ast.Expr) {
|
||||||
|
// nothing to do if x is not an identifier or the blank identifier
|
||||||
|
ident, _ := x.(*ast.Ident)
|
||||||
|
if ident == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert(ident.Obj == nil, "identifier already declared or resolved")
|
||||||
if ident.Name == "_" {
|
if ident.Name == "_" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -195,10 +201,12 @@ func (p *parser) resolve(ident *ast.Ident) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// collect unresolved global identifiers; ignore the others
|
// all local scopes are known, so any unresolved identifier
|
||||||
if p.topScope == p.pkgScope {
|
// must be found either in the file scope, package scope
|
||||||
p.unresolved = append(p.unresolved, ident)
|
// (perhaps in another file), or universe scope --- collect
|
||||||
}
|
// them so that they can be resolved later
|
||||||
|
ident.Obj = unresolved
|
||||||
|
p.unresolved = append(p.unresolved, ident)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -244,7 +252,7 @@ func (p *parser) next0() {
|
||||||
s := p.tok.String()
|
s := p.tok.String()
|
||||||
switch {
|
switch {
|
||||||
case p.tok.IsLiteral():
|
case p.tok.IsLiteral():
|
||||||
p.printTrace(s, string(p.lit_))
|
p.printTrace(s, p.lit)
|
||||||
case p.tok.IsOperator(), p.tok.IsKeyword():
|
case p.tok.IsOperator(), p.tok.IsKeyword():
|
||||||
p.printTrace("\"" + s + "\"")
|
p.printTrace("\"" + s + "\"")
|
||||||
default:
|
default:
|
||||||
|
@ -252,7 +260,7 @@ func (p *parser) next0() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.pos, p.tok, p.lit_ = p.scanner.Scan()
|
p.pos, p.tok, p.lit = p.scanner.Scan()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consume a comment and return it and the line on which it ends.
|
// Consume a comment and return it and the line on which it ends.
|
||||||
|
@ -260,15 +268,16 @@ func (p *parser) consumeComment() (comment *ast.Comment, endline int) {
|
||||||
// /*-style comments may end on a different line than where they start.
|
// /*-style comments may end on a different line than where they start.
|
||||||
// Scan the comment for '\n' chars and adjust endline accordingly.
|
// Scan the comment for '\n' chars and adjust endline accordingly.
|
||||||
endline = p.file.Line(p.pos)
|
endline = p.file.Line(p.pos)
|
||||||
if p.lit_[1] == '*' {
|
if p.lit[1] == '*' {
|
||||||
for _, b := range p.lit_ {
|
// don't use range here - no need to decode Unicode code points
|
||||||
if b == '\n' {
|
for i := 0; i < len(p.lit); i++ {
|
||||||
|
if p.lit[i] == '\n' {
|
||||||
endline++
|
endline++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
comment = &ast.Comment{p.pos, p.lit()}
|
comment = &ast.Comment{p.pos, p.lit}
|
||||||
p.next0()
|
p.next0()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -358,12 +367,12 @@ func (p *parser) errorExpected(pos token.Pos, msg string) {
|
||||||
if pos == p.pos {
|
if pos == p.pos {
|
||||||
// the error happened at the current position;
|
// the error happened at the current position;
|
||||||
// make the error message more specific
|
// make the error message more specific
|
||||||
if p.tok == token.SEMICOLON && p.lit_[0] == '\n' {
|
if p.tok == token.SEMICOLON && p.lit[0] == '\n' {
|
||||||
msg += ", found newline"
|
msg += ", found newline"
|
||||||
} else {
|
} else {
|
||||||
msg += ", found '" + p.tok.String() + "'"
|
msg += ", found '" + p.tok.String() + "'"
|
||||||
if p.tok.IsLiteral() {
|
if p.tok.IsLiteral() {
|
||||||
msg += " " + string(p.lit_)
|
msg += " " + p.lit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -388,6 +397,13 @@ func (p *parser) expectSemi() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func assert(cond bool, msg string) {
|
||||||
|
if !cond {
|
||||||
|
panic("go/parser internal error: " + msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Identifiers
|
// Identifiers
|
||||||
|
|
||||||
|
@ -395,7 +411,7 @@ func (p *parser) parseIdent() *ast.Ident {
|
||||||
pos := p.pos
|
pos := p.pos
|
||||||
name := "_"
|
name := "_"
|
||||||
if p.tok == token.IDENT {
|
if p.tok == token.IDENT {
|
||||||
name = string(p.lit_)
|
name = p.lit
|
||||||
p.next()
|
p.next()
|
||||||
} else {
|
} else {
|
||||||
p.expect(token.IDENT) // use expect() error handling
|
p.expect(token.IDENT) // use expect() error handling
|
||||||
|
@ -422,21 +438,51 @@ func (p *parser) parseIdentList() (list []*ast.Ident) {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Common productions
|
// Common productions
|
||||||
|
|
||||||
func (p *parser) parseExprList() (list []ast.Expr) {
|
// If lhs is set, result list elements which are identifiers are not resolved.
|
||||||
|
func (p *parser) parseExprList(lhs bool) (list []ast.Expr) {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "ExpressionList"))
|
defer un(trace(p, "ExpressionList"))
|
||||||
}
|
}
|
||||||
|
|
||||||
list = append(list, p.parseExpr())
|
list = append(list, p.parseExpr(lhs))
|
||||||
for p.tok == token.COMMA {
|
for p.tok == token.COMMA {
|
||||||
p.next()
|
p.next()
|
||||||
list = append(list, p.parseExpr())
|
list = append(list, p.parseExpr(lhs))
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *parser) parseLhsList() []ast.Expr {
|
||||||
|
list := p.parseExprList(true)
|
||||||
|
switch p.tok {
|
||||||
|
case token.DEFINE:
|
||||||
|
// lhs of a short variable declaration
|
||||||
|
p.shortVarDecl(p.makeIdentList(list))
|
||||||
|
case token.COLON:
|
||||||
|
// lhs of a label declaration or a communication clause of a select
|
||||||
|
// statement (parseLhsList is not called when parsing the case clause
|
||||||
|
// of a switch statement):
|
||||||
|
// - labels are declared by the caller of parseLhsList
|
||||||
|
// - for communication clauses, if there is a stand-alone identifier
|
||||||
|
// followed by a colon, we have a syntax error; there is no need
|
||||||
|
// to resolve the identifier in that case
|
||||||
|
default:
|
||||||
|
// identifiers must be declared elsewhere
|
||||||
|
for _, x := range list {
|
||||||
|
p.resolve(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *parser) parseRhsList() []ast.Expr {
|
||||||
|
return p.parseExprList(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Types
|
// Types
|
||||||
|
|
||||||
|
@ -458,31 +504,24 @@ func (p *parser) parseType() ast.Expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseQualifiedIdent() ast.Expr {
|
// If the result is an identifier, it is not resolved.
|
||||||
if p.trace {
|
|
||||||
defer un(trace(p, "QualifiedIdent"))
|
|
||||||
}
|
|
||||||
|
|
||||||
ident := p.parseIdent()
|
|
||||||
p.resolve(ident)
|
|
||||||
var x ast.Expr = ident
|
|
||||||
if p.tok == token.PERIOD {
|
|
||||||
// first identifier is a package identifier
|
|
||||||
p.next()
|
|
||||||
sel := p.parseIdent()
|
|
||||||
x = &ast.SelectorExpr{x, sel}
|
|
||||||
}
|
|
||||||
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseTypeName() ast.Expr {
|
func (p *parser) parseTypeName() ast.Expr {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "TypeName"))
|
defer un(trace(p, "TypeName"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.parseQualifiedIdent()
|
ident := p.parseIdent()
|
||||||
|
// don't resolve ident yet - it may be a parameter or field name
|
||||||
|
|
||||||
|
if p.tok == token.PERIOD {
|
||||||
|
// ident is a package name
|
||||||
|
p.next()
|
||||||
|
p.resolve(ident)
|
||||||
|
sel := p.parseIdent()
|
||||||
|
return &ast.SelectorExpr{ident, sel}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ident
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -497,7 +536,7 @@ func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr {
|
||||||
len = &ast.Ellipsis{p.pos, nil}
|
len = &ast.Ellipsis{p.pos, nil}
|
||||||
p.next()
|
p.next()
|
||||||
} else if p.tok != token.RBRACK {
|
} else if p.tok != token.RBRACK {
|
||||||
len = p.parseExpr()
|
len = p.parseRhs()
|
||||||
}
|
}
|
||||||
p.expect(token.RBRACK)
|
p.expect(token.RBRACK)
|
||||||
elt := p.parseType()
|
elt := p.parseType()
|
||||||
|
@ -521,7 +560,7 @@ func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseFieldDecl() *ast.Field {
|
func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "FieldDecl"))
|
defer un(trace(p, "FieldDecl"))
|
||||||
}
|
}
|
||||||
|
@ -534,7 +573,7 @@ func (p *parser) parseFieldDecl() *ast.Field {
|
||||||
// optional tag
|
// optional tag
|
||||||
var tag *ast.BasicLit
|
var tag *ast.BasicLit
|
||||||
if p.tok == token.STRING {
|
if p.tok == token.STRING {
|
||||||
tag = &ast.BasicLit{p.pos, p.tok, p.lit()}
|
tag = &ast.BasicLit{p.pos, p.tok, p.lit}
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,6 +585,7 @@ func (p *parser) parseFieldDecl() *ast.Field {
|
||||||
} else {
|
} else {
|
||||||
// ["*"] TypeName (AnonymousField)
|
// ["*"] TypeName (AnonymousField)
|
||||||
typ = list[0] // we always have at least one element
|
typ = list[0] // we always have at least one element
|
||||||
|
p.resolve(typ)
|
||||||
if n := len(list); n > 1 || !isTypeName(deref(typ)) {
|
if n := len(list); n > 1 || !isTypeName(deref(typ)) {
|
||||||
pos := typ.Pos()
|
pos := typ.Pos()
|
||||||
p.errorExpected(pos, "anonymous field")
|
p.errorExpected(pos, "anonymous field")
|
||||||
|
@ -555,7 +595,10 @@ func (p *parser) parseFieldDecl() *ast.Field {
|
||||||
|
|
||||||
p.expectSemi() // call before accessing p.linecomment
|
p.expectSemi() // call before accessing p.linecomment
|
||||||
|
|
||||||
return &ast.Field{doc, idents, typ, tag, p.lineComment}
|
field := &ast.Field{doc, idents, typ, tag, p.lineComment}
|
||||||
|
p.declare(field, scope, ast.Var, idents...)
|
||||||
|
|
||||||
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -566,15 +609,17 @@ func (p *parser) parseStructType() *ast.StructType {
|
||||||
|
|
||||||
pos := p.expect(token.STRUCT)
|
pos := p.expect(token.STRUCT)
|
||||||
lbrace := p.expect(token.LBRACE)
|
lbrace := p.expect(token.LBRACE)
|
||||||
|
scope := ast.NewScope(nil) // struct scope
|
||||||
var list []*ast.Field
|
var list []*ast.Field
|
||||||
for p.tok == token.IDENT || p.tok == token.MUL || p.tok == token.LPAREN {
|
for p.tok == token.IDENT || p.tok == token.MUL || p.tok == token.LPAREN {
|
||||||
// a field declaration cannot start with a '(' but we accept
|
// a field declaration cannot start with a '(' but we accept
|
||||||
// it here for more robust parsing and better error messages
|
// it here for more robust parsing and better error messages
|
||||||
// (parseFieldDecl will check and complain if necessary)
|
// (parseFieldDecl will check and complain if necessary)
|
||||||
list = append(list, p.parseFieldDecl())
|
list = append(list, p.parseFieldDecl(scope))
|
||||||
}
|
}
|
||||||
rbrace := p.expect(token.RBRACE)
|
rbrace := p.expect(token.RBRACE)
|
||||||
|
|
||||||
|
// TODO(gri): store struct scope in AST
|
||||||
return &ast.StructType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
|
return &ast.StructType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,7 +640,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
|
||||||
if isParam && p.tok == token.ELLIPSIS {
|
if isParam && p.tok == token.ELLIPSIS {
|
||||||
pos := p.pos
|
pos := p.pos
|
||||||
p.next()
|
p.next()
|
||||||
typ := p.tryType() // don't use parseType so we can provide better error message
|
typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message
|
||||||
if typ == nil {
|
if typ == nil {
|
||||||
p.error(pos, "'...' parameter is missing type")
|
p.error(pos, "'...' parameter is missing type")
|
||||||
typ = &ast.BadExpr{pos, p.pos}
|
typ = &ast.BadExpr{pos, p.pos}
|
||||||
|
@ -605,7 +650,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
|
||||||
}
|
}
|
||||||
return &ast.Ellipsis{pos, typ}
|
return &ast.Ellipsis{pos, typ}
|
||||||
}
|
}
|
||||||
return p.tryType()
|
return p.tryIdentOrType(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -641,6 +686,9 @@ func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
|
||||||
|
|
||||||
// if we had a list of identifiers, it must be followed by a type
|
// if we had a list of identifiers, it must be followed by a type
|
||||||
typ = p.tryVarType(isParam)
|
typ = p.tryVarType(isParam)
|
||||||
|
if typ != nil {
|
||||||
|
p.resolve(typ)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -682,6 +730,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
|
||||||
// Type { "," Type } (anonymous parameters)
|
// Type { "," Type } (anonymous parameters)
|
||||||
params = make([]*ast.Field, len(list))
|
params = make([]*ast.Field, len(list))
|
||||||
for i, x := range list {
|
for i, x := range list {
|
||||||
|
p.resolve(x)
|
||||||
params[i] = &ast.Field{Type: x}
|
params[i] = &ast.Field{Type: x}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -751,7 +800,7 @@ func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseMethodSpec() *ast.Field {
|
func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "MethodSpec"))
|
defer un(trace(p, "MethodSpec"))
|
||||||
}
|
}
|
||||||
|
@ -759,7 +808,7 @@ func (p *parser) parseMethodSpec() *ast.Field {
|
||||||
doc := p.leadComment
|
doc := p.leadComment
|
||||||
var idents []*ast.Ident
|
var idents []*ast.Ident
|
||||||
var typ ast.Expr
|
var typ ast.Expr
|
||||||
x := p.parseQualifiedIdent()
|
x := p.parseTypeName()
|
||||||
if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN {
|
if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN {
|
||||||
// method
|
// method
|
||||||
idents = []*ast.Ident{ident}
|
idents = []*ast.Ident{ident}
|
||||||
|
@ -772,7 +821,10 @@ func (p *parser) parseMethodSpec() *ast.Field {
|
||||||
}
|
}
|
||||||
p.expectSemi() // call before accessing p.linecomment
|
p.expectSemi() // call before accessing p.linecomment
|
||||||
|
|
||||||
return &ast.Field{doc, idents, typ, nil, p.lineComment}
|
spec := &ast.Field{doc, idents, typ, nil, p.lineComment}
|
||||||
|
p.declare(spec, scope, ast.Fun, idents...)
|
||||||
|
|
||||||
|
return spec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -783,12 +835,14 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
|
||||||
|
|
||||||
pos := p.expect(token.INTERFACE)
|
pos := p.expect(token.INTERFACE)
|
||||||
lbrace := p.expect(token.LBRACE)
|
lbrace := p.expect(token.LBRACE)
|
||||||
|
scope := ast.NewScope(nil) // interface scope
|
||||||
var list []*ast.Field
|
var list []*ast.Field
|
||||||
for p.tok == token.IDENT {
|
for p.tok == token.IDENT {
|
||||||
list = append(list, p.parseMethodSpec())
|
list = append(list, p.parseMethodSpec(scope))
|
||||||
}
|
}
|
||||||
rbrace := p.expect(token.RBRACE)
|
rbrace := p.expect(token.RBRACE)
|
||||||
|
|
||||||
|
// TODO(gri): store interface scope in AST
|
||||||
return &ast.InterfaceType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
|
return &ast.InterfaceType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -832,7 +886,8 @@ func (p *parser) parseChanType() *ast.ChanType {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
|
// If the result is an identifier, it is not resolved.
|
||||||
|
func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr {
|
||||||
switch p.tok {
|
switch p.tok {
|
||||||
case token.IDENT:
|
case token.IDENT:
|
||||||
return p.parseTypeName()
|
return p.parseTypeName()
|
||||||
|
@ -864,7 +919,13 @@ func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) tryType() ast.Expr { return p.tryRawType(false) }
|
func (p *parser) tryType() ast.Expr {
|
||||||
|
typ := p.tryIdentOrType(false)
|
||||||
|
if typ != nil {
|
||||||
|
p.resolve(typ)
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -939,20 +1000,23 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr {
|
||||||
|
|
||||||
// parseOperand may return an expression or a raw type (incl. array
|
// parseOperand may return an expression or a raw type (incl. array
|
||||||
// types of the form [...]T. Callers must verify the result.
|
// types of the form [...]T. Callers must verify the result.
|
||||||
|
// If lhs is set and the result is an identifier, it is not resolved.
|
||||||
//
|
//
|
||||||
func (p *parser) parseOperand() ast.Expr {
|
func (p *parser) parseOperand(lhs bool) ast.Expr {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "Operand"))
|
defer un(trace(p, "Operand"))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch p.tok {
|
switch p.tok {
|
||||||
case token.IDENT:
|
case token.IDENT:
|
||||||
ident := p.parseIdent()
|
x := p.parseIdent()
|
||||||
p.resolve(ident)
|
if !lhs {
|
||||||
return ident
|
p.resolve(x)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
|
||||||
case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING:
|
case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING:
|
||||||
x := &ast.BasicLit{p.pos, p.tok, p.lit()}
|
x := &ast.BasicLit{p.pos, p.tok, p.lit}
|
||||||
p.next()
|
p.next()
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
@ -960,7 +1024,7 @@ func (p *parser) parseOperand() ast.Expr {
|
||||||
lparen := p.pos
|
lparen := p.pos
|
||||||
p.next()
|
p.next()
|
||||||
p.exprLev++
|
p.exprLev++
|
||||||
x := p.parseExpr()
|
x := p.parseRhs()
|
||||||
p.exprLev--
|
p.exprLev--
|
||||||
rparen := p.expect(token.RPAREN)
|
rparen := p.expect(token.RPAREN)
|
||||||
return &ast.ParenExpr{lparen, x, rparen}
|
return &ast.ParenExpr{lparen, x, rparen}
|
||||||
|
@ -969,9 +1033,11 @@ func (p *parser) parseOperand() ast.Expr {
|
||||||
return p.parseFuncTypeOrLit()
|
return p.parseFuncTypeOrLit()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
t := p.tryRawType(true) // could be type for composite literal or conversion
|
if typ := p.tryIdentOrType(true); typ != nil {
|
||||||
if t != nil {
|
// could be type for composite literal or conversion
|
||||||
return t
|
_, isIdent := typ.(*ast.Ident)
|
||||||
|
assert(!isIdent, "type cannot be identifier")
|
||||||
|
return typ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -982,19 +1048,22 @@ func (p *parser) parseOperand() ast.Expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseSelectorOrTypeAssertion(x ast.Expr) ast.Expr {
|
func (p *parser) parseSelector(x ast.Expr) ast.Expr {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "SelectorOrTypeAssertion"))
|
defer un(trace(p, "Selector"))
|
||||||
}
|
}
|
||||||
|
|
||||||
p.expect(token.PERIOD)
|
sel := p.parseIdent()
|
||||||
if p.tok == token.IDENT {
|
|
||||||
// selector
|
return &ast.SelectorExpr{x, sel}
|
||||||
sel := p.parseIdent()
|
}
|
||||||
return &ast.SelectorExpr{x, sel}
|
|
||||||
|
|
||||||
|
func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr {
|
||||||
|
if p.trace {
|
||||||
|
defer un(trace(p, "TypeAssertion"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// type assertion
|
|
||||||
p.expect(token.LPAREN)
|
p.expect(token.LPAREN)
|
||||||
var typ ast.Expr
|
var typ ast.Expr
|
||||||
if p.tok == token.TYPE {
|
if p.tok == token.TYPE {
|
||||||
|
@ -1019,13 +1088,13 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
|
||||||
var low, high ast.Expr
|
var low, high ast.Expr
|
||||||
isSlice := false
|
isSlice := false
|
||||||
if p.tok != token.COLON {
|
if p.tok != token.COLON {
|
||||||
low = p.parseExpr()
|
low = p.parseRhs()
|
||||||
}
|
}
|
||||||
if p.tok == token.COLON {
|
if p.tok == token.COLON {
|
||||||
isSlice = true
|
isSlice = true
|
||||||
p.next()
|
p.next()
|
||||||
if p.tok != token.RBRACK {
|
if p.tok != token.RBRACK {
|
||||||
high = p.parseExpr()
|
high = p.parseRhs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.exprLev--
|
p.exprLev--
|
||||||
|
@ -1048,7 +1117,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
|
||||||
var list []ast.Expr
|
var list []ast.Expr
|
||||||
var ellipsis token.Pos
|
var ellipsis token.Pos
|
||||||
for p.tok != token.RPAREN && p.tok != token.EOF && !ellipsis.IsValid() {
|
for p.tok != token.RPAREN && p.tok != token.EOF && !ellipsis.IsValid() {
|
||||||
list = append(list, p.parseExpr())
|
list = append(list, p.parseRhs())
|
||||||
if p.tok == token.ELLIPSIS {
|
if p.tok == token.ELLIPSIS {
|
||||||
ellipsis = p.pos
|
ellipsis = p.pos
|
||||||
p.next()
|
p.next()
|
||||||
|
@ -1074,12 +1143,16 @@ func (p *parser) parseElement(keyOk bool) ast.Expr {
|
||||||
return p.parseLiteralValue(nil)
|
return p.parseLiteralValue(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
x := p.parseExpr()
|
x := p.parseExpr(keyOk) // don't resolve if map key
|
||||||
if keyOk && p.tok == token.COLON {
|
if keyOk {
|
||||||
colon := p.pos
|
if p.tok == token.COLON {
|
||||||
p.next()
|
colon := p.pos
|
||||||
x = &ast.KeyValueExpr{x, colon, p.parseElement(false)}
|
p.next()
|
||||||
|
return &ast.KeyValueExpr{x, colon, p.parseElement(false)}
|
||||||
|
}
|
||||||
|
p.resolve(x) // not a map key
|
||||||
}
|
}
|
||||||
|
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1231,23 +1304,47 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parsePrimaryExpr() ast.Expr {
|
// If lhs is set and the result is an identifier, it is not resolved.
|
||||||
|
func (p *parser) parsePrimaryExpr(lhs bool) ast.Expr {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "PrimaryExpr"))
|
defer un(trace(p, "PrimaryExpr"))
|
||||||
}
|
}
|
||||||
|
|
||||||
x := p.parseOperand()
|
x := p.parseOperand(lhs)
|
||||||
L:
|
L:
|
||||||
for {
|
for {
|
||||||
switch p.tok {
|
switch p.tok {
|
||||||
case token.PERIOD:
|
case token.PERIOD:
|
||||||
x = p.parseSelectorOrTypeAssertion(p.checkExpr(x))
|
p.next()
|
||||||
|
if lhs {
|
||||||
|
p.resolve(x)
|
||||||
|
}
|
||||||
|
switch p.tok {
|
||||||
|
case token.IDENT:
|
||||||
|
x = p.parseSelector(p.checkExpr(x))
|
||||||
|
case token.LPAREN:
|
||||||
|
x = p.parseTypeAssertion(p.checkExpr(x))
|
||||||
|
default:
|
||||||
|
pos := p.pos
|
||||||
|
p.next() // make progress
|
||||||
|
p.errorExpected(pos, "selector or type assertion")
|
||||||
|
x = &ast.BadExpr{pos, p.pos}
|
||||||
|
}
|
||||||
case token.LBRACK:
|
case token.LBRACK:
|
||||||
|
if lhs {
|
||||||
|
p.resolve(x)
|
||||||
|
}
|
||||||
x = p.parseIndexOrSlice(p.checkExpr(x))
|
x = p.parseIndexOrSlice(p.checkExpr(x))
|
||||||
case token.LPAREN:
|
case token.LPAREN:
|
||||||
|
if lhs {
|
||||||
|
p.resolve(x)
|
||||||
|
}
|
||||||
x = p.parseCallOrConversion(p.checkExprOrType(x))
|
x = p.parseCallOrConversion(p.checkExprOrType(x))
|
||||||
case token.LBRACE:
|
case token.LBRACE:
|
||||||
if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
|
if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
|
||||||
|
if lhs {
|
||||||
|
p.resolve(x)
|
||||||
|
}
|
||||||
x = p.parseLiteralValue(x)
|
x = p.parseLiteralValue(x)
|
||||||
} else {
|
} else {
|
||||||
break L
|
break L
|
||||||
|
@ -1255,13 +1352,15 @@ L:
|
||||||
default:
|
default:
|
||||||
break L
|
break L
|
||||||
}
|
}
|
||||||
|
lhs = false // no need to try to resolve again
|
||||||
}
|
}
|
||||||
|
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseUnaryExpr() ast.Expr {
|
// If lhs is set and the result is an identifier, it is not resolved.
|
||||||
|
func (p *parser) parseUnaryExpr(lhs bool) ast.Expr {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "UnaryExpr"))
|
defer un(trace(p, "UnaryExpr"))
|
||||||
}
|
}
|
||||||
|
@ -1270,7 +1369,7 @@ func (p *parser) parseUnaryExpr() ast.Expr {
|
||||||
case token.ADD, token.SUB, token.NOT, token.XOR, token.AND, token.RANGE:
|
case token.ADD, token.SUB, token.NOT, token.XOR, token.AND, token.RANGE:
|
||||||
pos, op := p.pos, p.tok
|
pos, op := p.pos, p.tok
|
||||||
p.next()
|
p.next()
|
||||||
x := p.parseUnaryExpr()
|
x := p.parseUnaryExpr(false)
|
||||||
return &ast.UnaryExpr{pos, op, p.checkExpr(x)}
|
return &ast.UnaryExpr{pos, op, p.checkExpr(x)}
|
||||||
|
|
||||||
case token.ARROW:
|
case token.ARROW:
|
||||||
|
@ -1283,32 +1382,37 @@ func (p *parser) parseUnaryExpr() ast.Expr {
|
||||||
return &ast.ChanType{pos, ast.RECV, value}
|
return &ast.ChanType{pos, ast.RECV, value}
|
||||||
}
|
}
|
||||||
|
|
||||||
x := p.parseUnaryExpr()
|
x := p.parseUnaryExpr(false)
|
||||||
return &ast.UnaryExpr{pos, token.ARROW, p.checkExpr(x)}
|
return &ast.UnaryExpr{pos, token.ARROW, p.checkExpr(x)}
|
||||||
|
|
||||||
case token.MUL:
|
case token.MUL:
|
||||||
// pointer type or unary "*" expression
|
// pointer type or unary "*" expression
|
||||||
pos := p.pos
|
pos := p.pos
|
||||||
p.next()
|
p.next()
|
||||||
x := p.parseUnaryExpr()
|
x := p.parseUnaryExpr(false)
|
||||||
return &ast.StarExpr{pos, p.checkExprOrType(x)}
|
return &ast.StarExpr{pos, p.checkExprOrType(x)}
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.parsePrimaryExpr()
|
return p.parsePrimaryExpr(lhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
|
// If lhs is set and the result is an identifier, it is not resolved.
|
||||||
|
func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "BinaryExpr"))
|
defer un(trace(p, "BinaryExpr"))
|
||||||
}
|
}
|
||||||
|
|
||||||
x := p.parseUnaryExpr()
|
x := p.parseUnaryExpr(lhs)
|
||||||
for prec := p.tok.Precedence(); prec >= prec1; prec-- {
|
for prec := p.tok.Precedence(); prec >= prec1; prec-- {
|
||||||
for p.tok.Precedence() == prec {
|
for p.tok.Precedence() == prec {
|
||||||
pos, op := p.pos, p.tok
|
pos, op := p.pos, p.tok
|
||||||
p.next()
|
p.next()
|
||||||
y := p.parseBinaryExpr(prec + 1)
|
if lhs {
|
||||||
|
p.resolve(x)
|
||||||
|
lhs = false
|
||||||
|
}
|
||||||
|
y := p.parseBinaryExpr(false, prec+1)
|
||||||
x = &ast.BinaryExpr{p.checkExpr(x), pos, op, p.checkExpr(y)}
|
x = &ast.BinaryExpr{p.checkExpr(x), pos, op, p.checkExpr(y)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1317,14 +1421,20 @@ func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If lhs is set and the result is an identifier, it is not resolved.
|
||||||
// TODO(gri): parseExpr may return a type or even a raw type ([..]int) -
|
// TODO(gri): parseExpr may return a type or even a raw type ([..]int) -
|
||||||
// should reject when a type/raw type is obviously not allowed
|
// should reject when a type/raw type is obviously not allowed
|
||||||
func (p *parser) parseExpr() ast.Expr {
|
func (p *parser) parseExpr(lhs bool) ast.Expr {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "Expression"))
|
defer un(trace(p, "Expression"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.parseBinaryExpr(token.LowestPrec + 1)
|
return p.parseBinaryExpr(lhs, token.LowestPrec+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *parser) parseRhs() ast.Expr {
|
||||||
|
return p.parseExpr(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1336,7 +1446,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
|
||||||
defer un(trace(p, "SimpleStmt"))
|
defer un(trace(p, "SimpleStmt"))
|
||||||
}
|
}
|
||||||
|
|
||||||
x := p.parseExprList()
|
x := p.parseLhsList()
|
||||||
|
|
||||||
switch p.tok {
|
switch p.tok {
|
||||||
case
|
case
|
||||||
|
@ -1347,10 +1457,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
|
||||||
// assignment statement
|
// assignment statement
|
||||||
pos, tok := p.pos, p.tok
|
pos, tok := p.pos, p.tok
|
||||||
p.next()
|
p.next()
|
||||||
y := p.parseExprList()
|
y := p.parseRhsList()
|
||||||
if tok == token.DEFINE {
|
|
||||||
p.shortVarDecl(p.makeIdentList(x))
|
|
||||||
}
|
|
||||||
return &ast.AssignStmt{x, pos, tok, y}
|
return &ast.AssignStmt{x, pos, tok, y}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1379,7 +1486,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
|
||||||
// send statement
|
// send statement
|
||||||
arrow := p.pos
|
arrow := p.pos
|
||||||
p.next() // consume "<-"
|
p.next() // consume "<-"
|
||||||
y := p.parseExpr()
|
y := p.parseRhs()
|
||||||
return &ast.SendStmt{x[0], arrow, y}
|
return &ast.SendStmt{x[0], arrow, y}
|
||||||
|
|
||||||
case token.INC, token.DEC:
|
case token.INC, token.DEC:
|
||||||
|
@ -1395,7 +1502,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseCallExpr() *ast.CallExpr {
|
func (p *parser) parseCallExpr() *ast.CallExpr {
|
||||||
x := p.parseExpr()
|
x := p.parseRhs()
|
||||||
if call, isCall := x.(*ast.CallExpr); isCall {
|
if call, isCall := x.(*ast.CallExpr); isCall {
|
||||||
return call
|
return call
|
||||||
}
|
}
|
||||||
|
@ -1445,7 +1552,7 @@ func (p *parser) parseReturnStmt() *ast.ReturnStmt {
|
||||||
p.expect(token.RETURN)
|
p.expect(token.RETURN)
|
||||||
var x []ast.Expr
|
var x []ast.Expr
|
||||||
if p.tok != token.SEMICOLON && p.tok != token.RBRACE {
|
if p.tok != token.SEMICOLON && p.tok != token.RBRACE {
|
||||||
x = p.parseExprList()
|
x = p.parseRhsList()
|
||||||
}
|
}
|
||||||
p.expectSemi()
|
p.expectSemi()
|
||||||
|
|
||||||
|
@ -1500,12 +1607,12 @@ func (p *parser) parseIfStmt() *ast.IfStmt {
|
||||||
p.exprLev = -1
|
p.exprLev = -1
|
||||||
if p.tok == token.SEMICOLON {
|
if p.tok == token.SEMICOLON {
|
||||||
p.next()
|
p.next()
|
||||||
x = p.parseExpr()
|
x = p.parseRhs()
|
||||||
} else {
|
} else {
|
||||||
s = p.parseSimpleStmt(false)
|
s = p.parseSimpleStmt(false)
|
||||||
if p.tok == token.SEMICOLON {
|
if p.tok == token.SEMICOLON {
|
||||||
p.next()
|
p.next()
|
||||||
x = p.parseExpr()
|
x = p.parseRhs()
|
||||||
} else {
|
} else {
|
||||||
x = p.makeExpr(s)
|
x = p.makeExpr(s)
|
||||||
s = nil
|
s = nil
|
||||||
|
@ -1552,7 +1659,7 @@ func (p *parser) parseCaseClause(exprSwitch bool) *ast.CaseClause {
|
||||||
if p.tok == token.CASE {
|
if p.tok == token.CASE {
|
||||||
p.next()
|
p.next()
|
||||||
if exprSwitch {
|
if exprSwitch {
|
||||||
list = p.parseExprList()
|
list = p.parseRhsList()
|
||||||
} else {
|
} else {
|
||||||
list = p.parseTypeList()
|
list = p.parseTypeList()
|
||||||
}
|
}
|
||||||
|
@ -1639,7 +1746,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
|
||||||
var comm ast.Stmt
|
var comm ast.Stmt
|
||||||
if p.tok == token.CASE {
|
if p.tok == token.CASE {
|
||||||
p.next()
|
p.next()
|
||||||
lhs := p.parseExprList()
|
lhs := p.parseLhsList()
|
||||||
if p.tok == token.ARROW {
|
if p.tok == token.ARROW {
|
||||||
// SendStmt
|
// SendStmt
|
||||||
if len(lhs) > 1 {
|
if len(lhs) > 1 {
|
||||||
|
@ -1648,7 +1755,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
|
||||||
}
|
}
|
||||||
arrow := p.pos
|
arrow := p.pos
|
||||||
p.next()
|
p.next()
|
||||||
rhs := p.parseExpr()
|
rhs := p.parseRhs()
|
||||||
comm = &ast.SendStmt{lhs[0], arrow, rhs}
|
comm = &ast.SendStmt{lhs[0], arrow, rhs}
|
||||||
} else {
|
} else {
|
||||||
// RecvStmt
|
// RecvStmt
|
||||||
|
@ -1663,10 +1770,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
|
||||||
lhs = lhs[0:2]
|
lhs = lhs[0:2]
|
||||||
}
|
}
|
||||||
p.next()
|
p.next()
|
||||||
rhs = p.parseExpr()
|
rhs = p.parseRhs()
|
||||||
if tok == token.DEFINE {
|
|
||||||
p.shortVarDecl(p.makeIdentList(lhs))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// rhs must be single receive operation
|
// rhs must be single receive operation
|
||||||
if len(lhs) > 1 {
|
if len(lhs) > 1 {
|
||||||
|
@ -1866,14 +1970,18 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
|
||||||
|
|
||||||
var path *ast.BasicLit
|
var path *ast.BasicLit
|
||||||
if p.tok == token.STRING {
|
if p.tok == token.STRING {
|
||||||
path = &ast.BasicLit{p.pos, p.tok, p.lit()}
|
path = &ast.BasicLit{p.pos, p.tok, p.lit}
|
||||||
p.next()
|
p.next()
|
||||||
} else {
|
} else {
|
||||||
p.expect(token.STRING) // use expect() error handling
|
p.expect(token.STRING) // use expect() error handling
|
||||||
}
|
}
|
||||||
p.expectSemi() // call before accessing p.linecomment
|
p.expectSemi() // call before accessing p.linecomment
|
||||||
|
|
||||||
return &ast.ImportSpec{doc, ident, path, p.lineComment}
|
// collect imports
|
||||||
|
spec := &ast.ImportSpec{doc, ident, path, p.lineComment}
|
||||||
|
p.imports = append(p.imports, spec)
|
||||||
|
|
||||||
|
return spec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1887,7 +1995,7 @@ func parseConstSpec(p *parser, doc *ast.CommentGroup, iota int) ast.Spec {
|
||||||
var values []ast.Expr
|
var values []ast.Expr
|
||||||
if typ != nil || p.tok == token.ASSIGN || iota == 0 {
|
if typ != nil || p.tok == token.ASSIGN || iota == 0 {
|
||||||
p.expect(token.ASSIGN)
|
p.expect(token.ASSIGN)
|
||||||
values = p.parseExprList()
|
values = p.parseRhsList()
|
||||||
}
|
}
|
||||||
p.expectSemi() // call before accessing p.linecomment
|
p.expectSemi() // call before accessing p.linecomment
|
||||||
|
|
||||||
|
@ -1932,7 +2040,7 @@ func parseVarSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
|
||||||
var values []ast.Expr
|
var values []ast.Expr
|
||||||
if typ == nil || p.tok == token.ASSIGN {
|
if typ == nil || p.tok == token.ASSIGN {
|
||||||
p.expect(token.ASSIGN)
|
p.expect(token.ASSIGN)
|
||||||
values = p.parseExprList()
|
values = p.parseRhsList()
|
||||||
}
|
}
|
||||||
p.expectSemi() // call before accessing p.linecomment
|
p.expectSemi() // call before accessing p.linecomment
|
||||||
|
|
||||||
|
@ -2120,20 +2228,20 @@ func (p *parser) parseFile() *ast.File {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.topScope != p.pkgScope {
|
assert(p.topScope == p.pkgScope, "imbalanced scopes")
|
||||||
panic("internal error: imbalanced scopes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolve global identifiers within the same file
|
// resolve global identifiers within the same file
|
||||||
i := 0
|
i := 0
|
||||||
for _, ident := range p.unresolved {
|
for _, ident := range p.unresolved {
|
||||||
// i <= index for current ident
|
// i <= index for current ident
|
||||||
ident.Obj = p.pkgScope.Lookup(ident.Name)
|
assert(ident.Obj == unresolved, "object already resolved")
|
||||||
|
ident.Obj = p.pkgScope.Lookup(ident.Name) // also removes unresolved sentinel
|
||||||
if ident.Obj == nil {
|
if ident.Obj == nil {
|
||||||
p.unresolved[i] = ident
|
p.unresolved[i] = ident
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ast.File{doc, pos, ident, decls, p.pkgScope, p.unresolved[0:i], p.comments}
|
// TODO(gri): store p.imports in AST
|
||||||
|
return &ast.File{doc, pos, ident, decls, p.pkgScope, p.imports, p.unresolved[0:i], p.comments}
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,19 +160,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
|
||||||
// the first linebreak is always a formfeed since this section must not
|
// the first linebreak is always a formfeed since this section must not
|
||||||
// depend on any previous formatting
|
// depend on any previous formatting
|
||||||
prevBreak := -1 // index of last expression that was followed by a linebreak
|
prevBreak := -1 // index of last expression that was followed by a linebreak
|
||||||
linebreakMin := 1
|
if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) {
|
||||||
if mode&periodSep != 0 {
|
|
||||||
// Make fragments like
|
|
||||||
//
|
|
||||||
// a.Bar(1,
|
|
||||||
// 2).Foo
|
|
||||||
//
|
|
||||||
// format correctly (a linebreak shouldn't be added before Foo) when
|
|
||||||
// doing period-separated expr lists by setting minimum linebreak to 0
|
|
||||||
// lines for them.
|
|
||||||
linebreakMin = 0
|
|
||||||
}
|
|
||||||
if prev.IsValid() && prev.Line < line && p.linebreak(line, linebreakMin, ws, true) {
|
|
||||||
ws = ignore
|
ws = ignore
|
||||||
*multiLine = true
|
*multiLine = true
|
||||||
prevBreak = 0
|
prevBreak = 0
|
||||||
|
@ -237,7 +225,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
|
||||||
// lines are broken using newlines so comments remain aligned
|
// lines are broken using newlines so comments remain aligned
|
||||||
// unless forceFF is set or there are multiple expressions on
|
// unless forceFF is set or there are multiple expressions on
|
||||||
// the same line in which case formfeed is used
|
// the same line in which case formfeed is used
|
||||||
if p.linebreak(line, linebreakMin, ws, useFF || prevBreak+1 < i) {
|
if p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) {
|
||||||
ws = ignore
|
ws = ignore
|
||||||
*multiLine = true
|
*multiLine = true
|
||||||
prevBreak = i
|
prevBreak = i
|
||||||
|
@ -363,7 +351,7 @@ func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
|
||||||
|
|
||||||
|
|
||||||
func (p *printer) setLineComment(text string) {
|
func (p *printer) setLineComment(text string) {
|
||||||
p.setComment(&ast.CommentGroup{[]*ast.Comment{&ast.Comment{token.NoPos, []byte(text)}}})
|
p.setComment(&ast.CommentGroup{[]*ast.Comment{&ast.Comment{token.NoPos, text}}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -527,7 +515,7 @@ func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.StarExpr:
|
case *ast.StarExpr:
|
||||||
if e.Op.String() == "/" {
|
if e.Op == token.QUO { // `*/`
|
||||||
maxProblem = 5
|
maxProblem = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,12 +34,6 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
const (
|
|
||||||
esc2 = '\xfe' // an escape byte that cannot occur in regular UTF-8
|
|
||||||
_ = 1 / (esc2 - tabwriter.Escape) // cause compiler error if esc2 == tabwriter.Escape
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
esc = []byte{tabwriter.Escape}
|
esc = []byte{tabwriter.Escape}
|
||||||
htab = []byte{'\t'}
|
htab = []byte{'\t'}
|
||||||
|
@ -81,8 +75,9 @@ type printer struct {
|
||||||
mode pmode // current printer mode
|
mode pmode // current printer mode
|
||||||
lastTok token.Token // the last token printed (token.ILLEGAL if it's whitespace)
|
lastTok token.Token // the last token printed (token.ILLEGAL if it's whitespace)
|
||||||
|
|
||||||
// Buffered whitespace
|
// Reused buffers
|
||||||
buffer []whiteSpace
|
wsbuf []whiteSpace // delayed white space
|
||||||
|
litbuf bytes.Buffer // for creation of escaped literals and comments
|
||||||
|
|
||||||
// The (possibly estimated) position in the generated output;
|
// The (possibly estimated) position in the generated output;
|
||||||
// in AST space (i.e., pos is set whenever a token position is
|
// in AST space (i.e., pos is set whenever a token position is
|
||||||
|
@ -109,7 +104,7 @@ func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet, nodeS
|
||||||
p.Config = *cfg
|
p.Config = *cfg
|
||||||
p.fset = fset
|
p.fset = fset
|
||||||
p.errors = make(chan os.Error)
|
p.errors = make(chan os.Error)
|
||||||
p.buffer = make([]whiteSpace, 0, 16) // whitespace sequences are short
|
p.wsbuf = make([]whiteSpace, 0, 16) // whitespace sequences are short
|
||||||
p.nodeSizes = nodeSizes
|
p.nodeSizes = nodeSizes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,6 +118,20 @@ func (p *printer) internalError(msg ...interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// escape escapes string s by bracketing it with tabwriter.Escape.
|
||||||
|
// Escaped strings pass through tabwriter unchanged. (Note that
|
||||||
|
// valid Go programs cannot contain tabwriter.Escape bytes since
|
||||||
|
// they do not appear in legal UTF-8 sequences).
|
||||||
|
//
|
||||||
|
func (p *printer) escape(s string) string {
|
||||||
|
p.litbuf.Reset()
|
||||||
|
p.litbuf.WriteByte(tabwriter.Escape)
|
||||||
|
p.litbuf.WriteString(s)
|
||||||
|
p.litbuf.WriteByte(tabwriter.Escape)
|
||||||
|
return p.litbuf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// nlines returns the adjusted number of linebreaks given the desired number
|
// nlines returns the adjusted number of linebreaks given the desired number
|
||||||
// of breaks n such that min <= result <= max where max depends on the current
|
// of breaks n such that min <= result <= max where max depends on the current
|
||||||
// nesting level.
|
// nesting level.
|
||||||
|
@ -230,7 +239,7 @@ func (p *printer) writeNewlines(n int, useFF bool) {
|
||||||
// source text. writeItem updates p.last to the position immediately following
|
// source text. writeItem updates p.last to the position immediately following
|
||||||
// the data.
|
// the data.
|
||||||
//
|
//
|
||||||
func (p *printer) writeItem(pos token.Position, data []byte) {
|
func (p *printer) writeItem(pos token.Position, data string) {
|
||||||
if pos.IsValid() {
|
if pos.IsValid() {
|
||||||
// continue with previous position if we don't have a valid pos
|
// continue with previous position if we don't have a valid pos
|
||||||
if p.last.IsValid() && p.last.Filename != pos.Filename {
|
if p.last.IsValid() && p.last.Filename != pos.Filename {
|
||||||
|
@ -239,7 +248,7 @@ func (p *printer) writeItem(pos token.Position, data []byte) {
|
||||||
// e.g., the result of ast.MergePackageFiles)
|
// e.g., the result of ast.MergePackageFiles)
|
||||||
p.indent = 0
|
p.indent = 0
|
||||||
p.mode = 0
|
p.mode = 0
|
||||||
p.buffer = p.buffer[0:0]
|
p.wsbuf = p.wsbuf[0:0]
|
||||||
}
|
}
|
||||||
p.pos = pos
|
p.pos = pos
|
||||||
}
|
}
|
||||||
|
@ -248,7 +257,7 @@ func (p *printer) writeItem(pos token.Position, data []byte) {
|
||||||
_, filename := filepath.Split(pos.Filename)
|
_, filename := filepath.Split(pos.Filename)
|
||||||
p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, pos.Column)))
|
p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, pos.Column)))
|
||||||
}
|
}
|
||||||
p.write(data)
|
p.write([]byte(data))
|
||||||
p.last = p.pos
|
p.last = p.pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,11 +289,11 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
|
||||||
if prev == nil {
|
if prev == nil {
|
||||||
// first comment of a comment group
|
// first comment of a comment group
|
||||||
j := 0
|
j := 0
|
||||||
for i, ch := range p.buffer {
|
for i, ch := range p.wsbuf {
|
||||||
switch ch {
|
switch ch {
|
||||||
case blank:
|
case blank:
|
||||||
// ignore any blanks before a comment
|
// ignore any blanks before a comment
|
||||||
p.buffer[i] = ignore
|
p.wsbuf[i] = ignore
|
||||||
continue
|
continue
|
||||||
case vtab:
|
case vtab:
|
||||||
// respect existing tabs - important
|
// respect existing tabs - important
|
||||||
|
@ -318,11 +327,11 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
|
||||||
if prev == nil {
|
if prev == nil {
|
||||||
// first comment of a comment group
|
// first comment of a comment group
|
||||||
j := 0
|
j := 0
|
||||||
for i, ch := range p.buffer {
|
for i, ch := range p.wsbuf {
|
||||||
switch ch {
|
switch ch {
|
||||||
case blank, vtab:
|
case blank, vtab:
|
||||||
// ignore any horizontal whitespace before line breaks
|
// ignore any horizontal whitespace before line breaks
|
||||||
p.buffer[i] = ignore
|
p.wsbuf[i] = ignore
|
||||||
continue
|
continue
|
||||||
case indent:
|
case indent:
|
||||||
// apply pending indentation
|
// apply pending indentation
|
||||||
|
@ -339,7 +348,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
|
||||||
}
|
}
|
||||||
case newline, formfeed:
|
case newline, formfeed:
|
||||||
// TODO(gri): may want to keep formfeed info in some cases
|
// TODO(gri): may want to keep formfeed info in some cases
|
||||||
p.buffer[i] = ignore
|
p.wsbuf[i] = ignore
|
||||||
}
|
}
|
||||||
j = i
|
j = i
|
||||||
break
|
break
|
||||||
|
@ -360,12 +369,8 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *printer) writeCommentLine(comment *ast.Comment, pos token.Position, line []byte) {
|
// TODO(gri): It should be possible to convert the code below from using
|
||||||
// line must pass through unchanged, bracket it with tabwriter.Escape
|
// []byte to string and in the process eliminate some conversions.
|
||||||
line = bytes.Join([][]byte{esc, line, esc}, nil)
|
|
||||||
p.writeItem(pos, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Split comment text into lines
|
// Split comment text into lines
|
||||||
func split(text []byte) [][]byte {
|
func split(text []byte) [][]byte {
|
||||||
|
@ -546,13 +551,13 @@ func (p *printer) writeComment(comment *ast.Comment) {
|
||||||
|
|
||||||
// shortcut common case of //-style comments
|
// shortcut common case of //-style comments
|
||||||
if text[1] == '/' {
|
if text[1] == '/' {
|
||||||
p.writeCommentLine(comment, p.fset.Position(comment.Pos()), text)
|
p.writeItem(p.fset.Position(comment.Pos()), p.escape(text))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// for /*-style comments, print line by line and let the
|
// for /*-style comments, print line by line and let the
|
||||||
// write function take care of the proper indentation
|
// write function take care of the proper indentation
|
||||||
lines := split(text)
|
lines := split([]byte(text))
|
||||||
stripCommonPrefix(lines)
|
stripCommonPrefix(lines)
|
||||||
|
|
||||||
// write comment lines, separated by formfeed,
|
// write comment lines, separated by formfeed,
|
||||||
|
@ -565,7 +570,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
|
||||||
pos = p.pos
|
pos = p.pos
|
||||||
}
|
}
|
||||||
if len(line) > 0 {
|
if len(line) > 0 {
|
||||||
p.writeCommentLine(comment, pos, line)
|
p.writeItem(pos, p.escape(string(line)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -578,11 +583,11 @@ func (p *printer) writeComment(comment *ast.Comment) {
|
||||||
// formfeed was dropped from the whitespace buffer.
|
// formfeed was dropped from the whitespace buffer.
|
||||||
//
|
//
|
||||||
func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
|
func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
|
||||||
for i, ch := range p.buffer {
|
for i, ch := range p.wsbuf {
|
||||||
switch ch {
|
switch ch {
|
||||||
case blank, vtab:
|
case blank, vtab:
|
||||||
// ignore trailing whitespace
|
// ignore trailing whitespace
|
||||||
p.buffer[i] = ignore
|
p.wsbuf[i] = ignore
|
||||||
case indent, unindent:
|
case indent, unindent:
|
||||||
// don't loose indentation information
|
// don't loose indentation information
|
||||||
case newline, formfeed:
|
case newline, formfeed:
|
||||||
|
@ -594,11 +599,11 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
|
||||||
if ch == formfeed {
|
if ch == formfeed {
|
||||||
droppedFF = true
|
droppedFF = true
|
||||||
}
|
}
|
||||||
p.buffer[i] = ignore
|
p.wsbuf[i] = ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.writeWhitespace(len(p.buffer))
|
p.writeWhitespace(len(p.wsbuf))
|
||||||
|
|
||||||
// make sure we have a line break
|
// make sure we have a line break
|
||||||
if needsLinebreak {
|
if needsLinebreak {
|
||||||
|
@ -652,7 +657,7 @@ func (p *printer) writeWhitespace(n int) {
|
||||||
// write entries
|
// write entries
|
||||||
var data [1]byte
|
var data [1]byte
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
switch ch := p.buffer[i]; ch {
|
switch ch := p.wsbuf[i]; ch {
|
||||||
case ignore:
|
case ignore:
|
||||||
// ignore!
|
// ignore!
|
||||||
case indent:
|
case indent:
|
||||||
|
@ -670,13 +675,13 @@ func (p *printer) writeWhitespace(n int) {
|
||||||
// the line break and the label, the unindent is not
|
// the line break and the label, the unindent is not
|
||||||
// part of the comment whitespace prefix and the comment
|
// part of the comment whitespace prefix and the comment
|
||||||
// will be positioned correctly indented.
|
// will be positioned correctly indented.
|
||||||
if i+1 < n && p.buffer[i+1] == unindent {
|
if i+1 < n && p.wsbuf[i+1] == unindent {
|
||||||
// Use a formfeed to terminate the current section.
|
// Use a formfeed to terminate the current section.
|
||||||
// Otherwise, a long label name on the next line leading
|
// Otherwise, a long label name on the next line leading
|
||||||
// to a wide column may increase the indentation column
|
// to a wide column may increase the indentation column
|
||||||
// of lines before the label; effectively leading to wrong
|
// of lines before the label; effectively leading to wrong
|
||||||
// indentation.
|
// indentation.
|
||||||
p.buffer[i], p.buffer[i+1] = unindent, formfeed
|
p.wsbuf[i], p.wsbuf[i+1] = unindent, formfeed
|
||||||
i-- // do it again
|
i-- // do it again
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -689,11 +694,11 @@ func (p *printer) writeWhitespace(n int) {
|
||||||
|
|
||||||
// shift remaining entries down
|
// shift remaining entries down
|
||||||
i := 0
|
i := 0
|
||||||
for ; n < len(p.buffer); n++ {
|
for ; n < len(p.wsbuf); n++ {
|
||||||
p.buffer[i] = p.buffer[n]
|
p.wsbuf[i] = p.wsbuf[n]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
p.buffer = p.buffer[0:i]
|
p.wsbuf = p.wsbuf[0:i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -734,7 +739,7 @@ func mayCombine(prev token.Token, next byte) (b bool) {
|
||||||
func (p *printer) print(args ...interface{}) {
|
func (p *printer) print(args ...interface{}) {
|
||||||
for _, f := range args {
|
for _, f := range args {
|
||||||
next := p.pos // estimated position of next item
|
next := p.pos // estimated position of next item
|
||||||
var data []byte
|
var data string
|
||||||
var tok token.Token
|
var tok token.Token
|
||||||
|
|
||||||
switch x := f.(type) {
|
switch x := f.(type) {
|
||||||
|
@ -748,42 +753,22 @@ func (p *printer) print(args ...interface{}) {
|
||||||
// LabeledStmt)
|
// LabeledStmt)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
i := len(p.buffer)
|
i := len(p.wsbuf)
|
||||||
if i == cap(p.buffer) {
|
if i == cap(p.wsbuf) {
|
||||||
// Whitespace sequences are very short so this should
|
// Whitespace sequences are very short so this should
|
||||||
// never happen. Handle gracefully (but possibly with
|
// never happen. Handle gracefully (but possibly with
|
||||||
// bad comment placement) if it does happen.
|
// bad comment placement) if it does happen.
|
||||||
p.writeWhitespace(i)
|
p.writeWhitespace(i)
|
||||||
i = 0
|
i = 0
|
||||||
}
|
}
|
||||||
p.buffer = p.buffer[0 : i+1]
|
p.wsbuf = p.wsbuf[0 : i+1]
|
||||||
p.buffer[i] = x
|
p.wsbuf[i] = x
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
data = []byte(x.Name)
|
data = x.Name
|
||||||
tok = token.IDENT
|
tok = token.IDENT
|
||||||
case *ast.BasicLit:
|
case *ast.BasicLit:
|
||||||
// escape all literals so they pass through unchanged
|
data = p.escape(x.Value)
|
||||||
// (note that valid Go programs cannot contain
|
|
||||||
// tabwriter.Escape bytes since they do not appear in
|
|
||||||
// legal UTF-8 sequences)
|
|
||||||
data = make([]byte, 0, len(x.Value)+2)
|
|
||||||
data = append(data, tabwriter.Escape)
|
|
||||||
data = append(data, x.Value...)
|
|
||||||
data = append(data, tabwriter.Escape)
|
|
||||||
tok = x.Kind
|
tok = x.Kind
|
||||||
// If we have a raw string that spans multiple lines and
|
|
||||||
// the opening quote (`) is on a line preceded only by
|
|
||||||
// indentation, we don't want to write that indentation
|
|
||||||
// because the following lines of the raw string are not
|
|
||||||
// indented. It's easiest to correct the output at the end
|
|
||||||
// via the trimmer (because of the complex handling of
|
|
||||||
// white space).
|
|
||||||
// Mark multi-line raw strings by replacing the opening
|
|
||||||
// quote with esc2 and have the trimmer take care of fixing
|
|
||||||
// it up. (Do this _after_ making a copy of data!)
|
|
||||||
if data[1] == '`' && bytes.IndexByte(data, '\n') > 0 {
|
|
||||||
data[1] = esc2
|
|
||||||
}
|
|
||||||
case token.Token:
|
case token.Token:
|
||||||
s := x.String()
|
s := x.String()
|
||||||
if mayCombine(p.lastTok, s[0]) {
|
if mayCombine(p.lastTok, s[0]) {
|
||||||
|
@ -793,13 +778,13 @@ func (p *printer) print(args ...interface{}) {
|
||||||
// (except for token.INT followed by a '.' this
|
// (except for token.INT followed by a '.' this
|
||||||
// should never happen because it is taken care
|
// should never happen because it is taken care
|
||||||
// of via binary expression formatting)
|
// of via binary expression formatting)
|
||||||
if len(p.buffer) != 0 {
|
if len(p.wsbuf) != 0 {
|
||||||
p.internalError("whitespace buffer not empty")
|
p.internalError("whitespace buffer not empty")
|
||||||
}
|
}
|
||||||
p.buffer = p.buffer[0:1]
|
p.wsbuf = p.wsbuf[0:1]
|
||||||
p.buffer[0] = ' '
|
p.wsbuf[0] = ' '
|
||||||
}
|
}
|
||||||
data = []byte(s)
|
data = s
|
||||||
tok = x
|
tok = x
|
||||||
case token.Pos:
|
case token.Pos:
|
||||||
if x.IsValid() {
|
if x.IsValid() {
|
||||||
|
@ -813,7 +798,7 @@ func (p *printer) print(args ...interface{}) {
|
||||||
p.lastTok = tok
|
p.lastTok = tok
|
||||||
p.pos = next
|
p.pos = next
|
||||||
|
|
||||||
if data != nil {
|
if data != "" {
|
||||||
droppedFF := p.flush(next, tok)
|
droppedFF := p.flush(next, tok)
|
||||||
|
|
||||||
// intersperse extra newlines if present in the source
|
// intersperse extra newlines if present in the source
|
||||||
|
@ -848,7 +833,7 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) {
|
||||||
droppedFF = p.intersperseComments(next, tok)
|
droppedFF = p.intersperseComments(next, tok)
|
||||||
} else {
|
} else {
|
||||||
// otherwise, write any leftover whitespace
|
// otherwise, write any leftover whitespace
|
||||||
p.writeWhitespace(len(p.buffer))
|
p.writeWhitespace(len(p.wsbuf))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -864,10 +849,9 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) {
|
||||||
// through unchanged.
|
// through unchanged.
|
||||||
//
|
//
|
||||||
type trimmer struct {
|
type trimmer struct {
|
||||||
output io.Writer
|
output io.Writer
|
||||||
state int
|
state int
|
||||||
space bytes.Buffer
|
space bytes.Buffer
|
||||||
hasText bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -875,15 +859,11 @@ type trimmer struct {
|
||||||
// It can be in one of the following states:
|
// It can be in one of the following states:
|
||||||
const (
|
const (
|
||||||
inSpace = iota // inside space
|
inSpace = iota // inside space
|
||||||
atEscape // inside space and the last char was an opening tabwriter.Escape
|
|
||||||
inEscape // inside text bracketed by tabwriter.Escapes
|
inEscape // inside text bracketed by tabwriter.Escapes
|
||||||
inText // inside text
|
inText // inside text
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
var backquote = []byte{'`'}
|
|
||||||
|
|
||||||
|
|
||||||
// Design note: It is tempting to eliminate extra blanks occurring in
|
// Design note: It is tempting to eliminate extra blanks occurring in
|
||||||
// whitespace in this function as it could simplify some
|
// whitespace in this function as it could simplify some
|
||||||
// of the blanks logic in the node printing functions.
|
// of the blanks logic in the node printing functions.
|
||||||
|
@ -892,9 +872,8 @@ var backquote = []byte{'`'}
|
||||||
|
|
||||||
func (p *trimmer) Write(data []byte) (n int, err os.Error) {
|
func (p *trimmer) Write(data []byte) (n int, err os.Error) {
|
||||||
// invariants:
|
// invariants:
|
||||||
// p.state == inSpace, atEscape:
|
// p.state == inSpace:
|
||||||
// p.space is unwritten
|
// p.space is unwritten
|
||||||
// p.hasText indicates if there is any text on this line
|
|
||||||
// p.state == inEscape, inText:
|
// p.state == inEscape, inText:
|
||||||
// data[m:n] is unwritten
|
// data[m:n] is unwritten
|
||||||
m := 0
|
m := 0
|
||||||
|
@ -911,32 +890,20 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) {
|
||||||
case '\n', '\f':
|
case '\n', '\f':
|
||||||
p.space.Reset() // discard trailing space
|
p.space.Reset() // discard trailing space
|
||||||
_, err = p.output.Write(newlines[0:1]) // write newline
|
_, err = p.output.Write(newlines[0:1]) // write newline
|
||||||
p.hasText = false
|
|
||||||
case tabwriter.Escape:
|
case tabwriter.Escape:
|
||||||
p.state = atEscape
|
_, err = p.output.Write(p.space.Bytes())
|
||||||
|
p.state = inEscape
|
||||||
|
m = n + 1 // +1: skip tabwriter.Escape
|
||||||
default:
|
default:
|
||||||
_, err = p.output.Write(p.space.Bytes())
|
_, err = p.output.Write(p.space.Bytes())
|
||||||
p.state = inText
|
p.state = inText
|
||||||
m = n
|
m = n
|
||||||
}
|
}
|
||||||
case atEscape:
|
|
||||||
// discard indentation if we have a multi-line raw string
|
|
||||||
// (see printer.print for details)
|
|
||||||
if b != esc2 || p.hasText {
|
|
||||||
_, err = p.output.Write(p.space.Bytes())
|
|
||||||
}
|
|
||||||
p.state = inEscape
|
|
||||||
m = n
|
|
||||||
if b == esc2 {
|
|
||||||
_, err = p.output.Write(backquote) // convert back
|
|
||||||
m++
|
|
||||||
}
|
|
||||||
case inEscape:
|
case inEscape:
|
||||||
if b == tabwriter.Escape {
|
if b == tabwriter.Escape {
|
||||||
_, err = p.output.Write(data[m:n])
|
_, err = p.output.Write(data[m:n])
|
||||||
p.state = inSpace
|
p.state = inSpace
|
||||||
p.space.Reset()
|
p.space.Reset()
|
||||||
p.hasText = true
|
|
||||||
}
|
}
|
||||||
case inText:
|
case inText:
|
||||||
switch b {
|
switch b {
|
||||||
|
@ -945,19 +912,18 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) {
|
||||||
p.state = inSpace
|
p.state = inSpace
|
||||||
p.space.Reset()
|
p.space.Reset()
|
||||||
p.space.WriteByte(b) // WriteByte returns no errors
|
p.space.WriteByte(b) // WriteByte returns no errors
|
||||||
p.hasText = true
|
|
||||||
case '\n', '\f':
|
case '\n', '\f':
|
||||||
_, err = p.output.Write(data[m:n])
|
_, err = p.output.Write(data[m:n])
|
||||||
p.state = inSpace
|
p.state = inSpace
|
||||||
p.space.Reset()
|
p.space.Reset()
|
||||||
_, err = p.output.Write(newlines[0:1]) // write newline
|
_, err = p.output.Write(newlines[0:1]) // write newline
|
||||||
p.hasText = false
|
|
||||||
case tabwriter.Escape:
|
case tabwriter.Escape:
|
||||||
_, err = p.output.Write(data[m:n])
|
_, err = p.output.Write(data[m:n])
|
||||||
p.state = atEscape
|
p.state = inEscape
|
||||||
p.space.Reset()
|
m = n + 1 // +1: skip tabwriter.Escape
|
||||||
p.hasText = true
|
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -970,7 +936,6 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) {
|
||||||
_, err = p.output.Write(data[m:n])
|
_, err = p.output.Write(data[m:n])
|
||||||
p.state = inSpace
|
p.state = inSpace
|
||||||
p.space.Reset()
|
p.space.Reset()
|
||||||
p.hasText = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -114,7 +114,7 @@ func check(t *testing.T, source, golden string, mode checkMode) {
|
||||||
// start a timer to produce a time-out signal
|
// start a timer to produce a time-out signal
|
||||||
tc := make(chan int)
|
tc := make(chan int)
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(20e9) // plenty of a safety margin, even for very slow machines
|
time.Sleep(10e9) // plenty of a safety margin, even for very slow machines
|
||||||
tc <- 0
|
tc <- 0
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -156,12 +156,15 @@ var data = []entry{
|
||||||
|
|
||||||
|
|
||||||
func TestFiles(t *testing.T) {
|
func TestFiles(t *testing.T) {
|
||||||
for _, e := range data {
|
for i, e := range data {
|
||||||
source := filepath.Join(dataDir, e.source)
|
source := filepath.Join(dataDir, e.source)
|
||||||
golden := filepath.Join(dataDir, e.golden)
|
golden := filepath.Join(dataDir, e.golden)
|
||||||
check(t, source, golden, e.mode)
|
check(t, source, golden, e.mode)
|
||||||
// TODO(gri) check that golden is idempotent
|
// TODO(gri) check that golden is idempotent
|
||||||
//check(t, golden, golden, e.mode);
|
//check(t, golden, golden, e.mode)
|
||||||
|
if testing.Short() && i >= 3 {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -253,8 +253,8 @@ bar`
|
||||||
var _ = ``
|
var _ = ``
|
||||||
var _ = `foo`
|
var _ = `foo`
|
||||||
var _ =
|
var _ =
|
||||||
// the next line should not be indented
|
// the next line should remain indented
|
||||||
`foo
|
`foo
|
||||||
bar`
|
bar`
|
||||||
|
|
||||||
var _ = // comment
|
var _ = // comment
|
||||||
|
@ -262,8 +262,8 @@ bar`
|
||||||
var _ = // comment
|
var _ = // comment
|
||||||
`foo`
|
`foo`
|
||||||
var _ = // comment
|
var _ = // comment
|
||||||
// the next line should not be indented
|
// the next line should remain indented
|
||||||
`foo
|
`foo
|
||||||
bar`
|
bar`
|
||||||
|
|
||||||
var _ = /* comment */ ``
|
var _ = /* comment */ ``
|
||||||
|
@ -276,12 +276,12 @@ bar`
|
||||||
var _ = /* comment */
|
var _ = /* comment */
|
||||||
`foo`
|
`foo`
|
||||||
var _ = /* comment */
|
var _ = /* comment */
|
||||||
// the next line should not be indented
|
// the next line should remain indented
|
||||||
`foo
|
`foo
|
||||||
bar`
|
bar`
|
||||||
|
|
||||||
var board = []int(
|
var board = []int(
|
||||||
`...........
|
`...........
|
||||||
...........
|
...........
|
||||||
....●●●....
|
....●●●....
|
||||||
....●●●....
|
....●●●....
|
||||||
|
@ -296,8 +296,8 @@ bar`
|
||||||
|
|
||||||
var state = S{
|
var state = S{
|
||||||
"foo",
|
"foo",
|
||||||
// the next line should not be indented
|
// the next line should remain indented
|
||||||
`...........
|
`...........
|
||||||
...........
|
...........
|
||||||
....●●●....
|
....●●●....
|
||||||
....●●●....
|
....●●●....
|
||||||
|
@ -619,3 +619,13 @@ func _() {
|
||||||
b.(T).
|
b.(T).
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Don't introduce extra newlines in strangely formatted expression lists.
|
||||||
|
func f() {
|
||||||
|
// os.Open parameters should remain on two lines
|
||||||
|
if writer, err = os.Open(outfile, s.O_WRONLY|os.O_CREATE|
|
||||||
|
os.O_TRUNC,0666); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -256,7 +256,7 @@ var _ =
|
||||||
var _ =
|
var _ =
|
||||||
`foo`
|
`foo`
|
||||||
var _ =
|
var _ =
|
||||||
// the next line should not be indented
|
// the next line should remain indented
|
||||||
`foo
|
`foo
|
||||||
bar`
|
bar`
|
||||||
|
|
||||||
|
@ -266,7 +266,7 @@ bar`
|
||||||
var _ = // comment
|
var _ = // comment
|
||||||
`foo`
|
`foo`
|
||||||
var _ = // comment
|
var _ = // comment
|
||||||
// the next line should not be indented
|
// the next line should remain indented
|
||||||
`foo
|
`foo
|
||||||
bar`
|
bar`
|
||||||
|
|
||||||
|
@ -282,7 +282,7 @@ bar`
|
||||||
var _ = /* comment */
|
var _ = /* comment */
|
||||||
`foo`
|
`foo`
|
||||||
var _ = /* comment */
|
var _ = /* comment */
|
||||||
// the next line should not be indented
|
// the next line should remain indented
|
||||||
`foo
|
`foo
|
||||||
bar`
|
bar`
|
||||||
|
|
||||||
|
@ -304,7 +304,7 @@ var board = []int(
|
||||||
|
|
||||||
var state = S{
|
var state = S{
|
||||||
"foo",
|
"foo",
|
||||||
// the next line should not be indented
|
// the next line should remain indented
|
||||||
`...........
|
`...........
|
||||||
...........
|
...........
|
||||||
....●●●....
|
....●●●....
|
||||||
|
@ -625,3 +625,13 @@ baz()
|
||||||
(T).
|
(T).
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Don't introduce extra newlines in strangely formatted expression lists.
|
||||||
|
func f() {
|
||||||
|
// os.Open parameters should remain on two lines
|
||||||
|
if writer, err = os.Open(outfile, s.O_WRONLY|os.O_CREATE|
|
||||||
|
os.O_TRUNC, 0666); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -239,7 +239,8 @@ func _() {
|
||||||
_ = `foo
|
_ = `foo
|
||||||
bar`
|
bar`
|
||||||
_ = `three spaces before the end of the line starting here:
|
_ = `three spaces before the end of the line starting here:
|
||||||
they must not be removed`}
|
they must not be removed`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func _() {
|
func _() {
|
||||||
|
@ -252,8 +253,8 @@ bar`
|
||||||
var _ = ``
|
var _ = ``
|
||||||
var _ = `foo`
|
var _ = `foo`
|
||||||
var _ =
|
var _ =
|
||||||
// the next line should not be indented
|
// the next line should remain indented
|
||||||
`foo
|
`foo
|
||||||
bar`
|
bar`
|
||||||
|
|
||||||
var _ = // comment
|
var _ = // comment
|
||||||
|
@ -261,8 +262,8 @@ bar`
|
||||||
var _ = // comment
|
var _ = // comment
|
||||||
`foo`
|
`foo`
|
||||||
var _ = // comment
|
var _ = // comment
|
||||||
// the next line should not be indented
|
// the next line should remain indented
|
||||||
`foo
|
`foo
|
||||||
bar`
|
bar`
|
||||||
|
|
||||||
var _ = /* comment */ ``
|
var _ = /* comment */ ``
|
||||||
|
@ -275,12 +276,12 @@ bar`
|
||||||
var _ = /* comment */
|
var _ = /* comment */
|
||||||
`foo`
|
`foo`
|
||||||
var _ = /* comment */
|
var _ = /* comment */
|
||||||
// the next line should not be indented
|
// the next line should remain indented
|
||||||
`foo
|
`foo
|
||||||
bar`
|
bar`
|
||||||
|
|
||||||
var board = []int(
|
var board = []int(
|
||||||
`...........
|
`...........
|
||||||
...........
|
...........
|
||||||
....●●●....
|
....●●●....
|
||||||
....●●●....
|
....●●●....
|
||||||
|
@ -295,8 +296,8 @@ bar`
|
||||||
|
|
||||||
var state = S{
|
var state = S{
|
||||||
"foo",
|
"foo",
|
||||||
// the next line should not be indented
|
// the next line should remain indented
|
||||||
`...........
|
`...........
|
||||||
...........
|
...........
|
||||||
....●●●....
|
....●●●....
|
||||||
....●●●....
|
....●●●....
|
||||||
|
@ -618,3 +619,13 @@ func _() {
|
||||||
b.(T).
|
b.(T).
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Don't introduce extra newlines in strangely formatted expression lists.
|
||||||
|
func f() {
|
||||||
|
// os.Open parameters should remain on two lines
|
||||||
|
if writer, err = os.Open(outfile, s.O_WRONLY|os.O_CREATE|
|
||||||
|
os.O_TRUNC,0666); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -538,14 +538,12 @@ func (S *Scanner) switch4(tok0, tok1 token.Token, ch2 int, tok2, tok3 token.Toke
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var newline = []byte{'\n'}
|
// Scan scans the next token and returns the token position,
|
||||||
|
// the token, and the literal string corresponding to the
|
||||||
// Scan scans the next token and returns the token position pos,
|
|
||||||
// the token tok, and the literal text lit corresponding to the
|
|
||||||
// token. The source end is indicated by token.EOF.
|
// token. The source end is indicated by token.EOF.
|
||||||
//
|
//
|
||||||
// If the returned token is token.SEMICOLON, the corresponding
|
// If the returned token is token.SEMICOLON, the corresponding
|
||||||
// literal value is ";" if the semicolon was present in the source,
|
// literal string is ";" if the semicolon was present in the source,
|
||||||
// and "\n" if the semicolon was inserted because of a newline or
|
// and "\n" if the semicolon was inserted because of a newline or
|
||||||
// at EOF.
|
// at EOF.
|
||||||
//
|
//
|
||||||
|
@ -560,7 +558,7 @@ var newline = []byte{'\n'}
|
||||||
// set with Init. Token positions are relative to that file
|
// set with Init. Token positions are relative to that file
|
||||||
// and thus relative to the file set.
|
// and thus relative to the file set.
|
||||||
//
|
//
|
||||||
func (S *Scanner) Scan() (token.Pos, token.Token, []byte) {
|
func (S *Scanner) Scan() (token.Pos, token.Token, string) {
|
||||||
scanAgain:
|
scanAgain:
|
||||||
S.skipWhitespace()
|
S.skipWhitespace()
|
||||||
|
|
||||||
|
@ -586,7 +584,7 @@ scanAgain:
|
||||||
case -1:
|
case -1:
|
||||||
if S.insertSemi {
|
if S.insertSemi {
|
||||||
S.insertSemi = false // EOF consumed
|
S.insertSemi = false // EOF consumed
|
||||||
return S.file.Pos(offs), token.SEMICOLON, newline
|
return S.file.Pos(offs), token.SEMICOLON, "\n"
|
||||||
}
|
}
|
||||||
tok = token.EOF
|
tok = token.EOF
|
||||||
case '\n':
|
case '\n':
|
||||||
|
@ -594,7 +592,7 @@ scanAgain:
|
||||||
// set in the first place and exited early
|
// set in the first place and exited early
|
||||||
// from S.skipWhitespace()
|
// from S.skipWhitespace()
|
||||||
S.insertSemi = false // newline consumed
|
S.insertSemi = false // newline consumed
|
||||||
return S.file.Pos(offs), token.SEMICOLON, newline
|
return S.file.Pos(offs), token.SEMICOLON, "\n"
|
||||||
case '"':
|
case '"':
|
||||||
insertSemi = true
|
insertSemi = true
|
||||||
tok = token.STRING
|
tok = token.STRING
|
||||||
|
@ -662,7 +660,7 @@ scanAgain:
|
||||||
S.offset = offs
|
S.offset = offs
|
||||||
S.rdOffset = offs + 1
|
S.rdOffset = offs + 1
|
||||||
S.insertSemi = false // newline consumed
|
S.insertSemi = false // newline consumed
|
||||||
return S.file.Pos(offs), token.SEMICOLON, newline
|
return S.file.Pos(offs), token.SEMICOLON, "\n"
|
||||||
}
|
}
|
||||||
S.scanComment()
|
S.scanComment()
|
||||||
if S.mode&ScanComments == 0 {
|
if S.mode&ScanComments == 0 {
|
||||||
|
@ -711,5 +709,9 @@ scanAgain:
|
||||||
if S.mode&InsertSemis != 0 {
|
if S.mode&InsertSemis != 0 {
|
||||||
S.insertSemi = insertSemi
|
S.insertSemi = insertSemi
|
||||||
}
|
}
|
||||||
return S.file.Pos(offs), tok, S.src[offs:S.offset]
|
|
||||||
|
// TODO(gri): The scanner API should change such that the literal string
|
||||||
|
// is only valid if an actual literal was scanned. This will
|
||||||
|
// permit a more efficient implementation.
|
||||||
|
return S.file.Pos(offs), tok, string(S.src[offs:S.offset])
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,12 +234,11 @@ func TestScan(t *testing.T) {
|
||||||
index := 0
|
index := 0
|
||||||
epos := token.Position{"", 0, 1, 1} // expected position
|
epos := token.Position{"", 0, 1, 1} // expected position
|
||||||
for {
|
for {
|
||||||
pos, tok, litb := s.Scan()
|
pos, tok, lit := s.Scan()
|
||||||
e := elt{token.EOF, "", special}
|
e := elt{token.EOF, "", special}
|
||||||
if index < len(tokens) {
|
if index < len(tokens) {
|
||||||
e = tokens[index]
|
e = tokens[index]
|
||||||
}
|
}
|
||||||
lit := string(litb)
|
|
||||||
if tok == token.EOF {
|
if tok == token.EOF {
|
||||||
lit = "<EOF>"
|
lit = "<EOF>"
|
||||||
epos.Line = src_linecount
|
epos.Line = src_linecount
|
||||||
|
@ -257,7 +256,7 @@ func TestScan(t *testing.T) {
|
||||||
}
|
}
|
||||||
epos.Offset += len(lit) + len(whitespace)
|
epos.Offset += len(lit) + len(whitespace)
|
||||||
epos.Line += newlineCount(lit) + whitespace_linecount
|
epos.Line += newlineCount(lit) + whitespace_linecount
|
||||||
if tok == token.COMMENT && litb[1] == '/' {
|
if tok == token.COMMENT && lit[1] == '/' {
|
||||||
// correct for unaccounted '/n' in //-style comment
|
// correct for unaccounted '/n' in //-style comment
|
||||||
epos.Offset++
|
epos.Offset++
|
||||||
epos.Line++
|
epos.Line++
|
||||||
|
@ -292,7 +291,7 @@ func checkSemi(t *testing.T, line string, mode uint) {
|
||||||
semiPos.Column++
|
semiPos.Column++
|
||||||
pos, tok, lit = S.Scan()
|
pos, tok, lit = S.Scan()
|
||||||
if tok == token.SEMICOLON {
|
if tok == token.SEMICOLON {
|
||||||
if string(lit) != semiLit {
|
if lit != semiLit {
|
||||||
t.Errorf(`bad literal for %q: got %q, expected %q`, line, lit, semiLit)
|
t.Errorf(`bad literal for %q: got %q, expected %q`, line, lit, semiLit)
|
||||||
}
|
}
|
||||||
checkPos(t, line, pos, semiPos)
|
checkPos(t, line, pos, semiPos)
|
||||||
|
@ -493,7 +492,7 @@ func TestLineComments(t *testing.T) {
|
||||||
for _, s := range segments {
|
for _, s := range segments {
|
||||||
p, _, lit := S.Scan()
|
p, _, lit := S.Scan()
|
||||||
pos := file.Position(p)
|
pos := file.Position(p)
|
||||||
checkPos(t, string(lit), p, token.Position{s.filename, pos.Offset, s.line, pos.Column})
|
checkPos(t, lit, p, token.Position{s.filename, pos.Offset, s.line, pos.Column})
|
||||||
}
|
}
|
||||||
|
|
||||||
if S.ErrorCount != 0 {
|
if S.ErrorCount != 0 {
|
||||||
|
@ -547,10 +546,10 @@ func TestIllegalChars(t *testing.T) {
|
||||||
for offs, ch := range src {
|
for offs, ch := range src {
|
||||||
pos, tok, lit := s.Scan()
|
pos, tok, lit := s.Scan()
|
||||||
if poffs := file.Offset(pos); poffs != offs {
|
if poffs := file.Offset(pos); poffs != offs {
|
||||||
t.Errorf("bad position for %s: got %d, expected %d", string(lit), poffs, offs)
|
t.Errorf("bad position for %s: got %d, expected %d", lit, poffs, offs)
|
||||||
}
|
}
|
||||||
if tok == token.ILLEGAL && string(lit) != string(ch) {
|
if tok == token.ILLEGAL && lit != string(ch) {
|
||||||
t.Errorf("bad token: got %s, expected %s", string(lit), string(ch))
|
t.Errorf("bad token: got %s, expected %s", lit, string(ch))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,10 +126,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// At the moment we have no array literal syntax that lets us describe
|
var tokens = [...]string{
|
||||||
// the index for each element - use a map for now to make sure they are
|
|
||||||
// in sync.
|
|
||||||
var tokens = map[Token]string{
|
|
||||||
ILLEGAL: "ILLEGAL",
|
ILLEGAL: "ILLEGAL",
|
||||||
|
|
||||||
EOF: "EOF",
|
EOF: "EOF",
|
||||||
|
@ -237,10 +234,14 @@ var tokens = map[Token]string{
|
||||||
// constant name (e.g. for the token IDENT, the string is "IDENT").
|
// constant name (e.g. for the token IDENT, the string is "IDENT").
|
||||||
//
|
//
|
||||||
func (tok Token) String() string {
|
func (tok Token) String() string {
|
||||||
if str, exists := tokens[tok]; exists {
|
s := ""
|
||||||
return str
|
if 0 <= tok && tok < Token(len(tokens)) {
|
||||||
|
s = tokens[tok]
|
||||||
}
|
}
|
||||||
return "token(" + strconv.Itoa(int(tok)) + ")"
|
if s == "" {
|
||||||
|
s = "token(" + strconv.Itoa(int(tok)) + ")"
|
||||||
|
}
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.ObjKind, name *ast
|
||||||
//obj.N = n
|
//obj.N = n
|
||||||
name.Obj = obj
|
name.Obj = obj
|
||||||
if name.Name != "_" {
|
if name.Name != "_" {
|
||||||
if alt := scope.Insert(obj); alt != obj {
|
if alt := scope.Insert(obj); alt != nil {
|
||||||
tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, tc.fset.Position(alt.Pos()).String())
|
tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, tc.fset.Position(alt.Pos()).String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ func CheckPackage(fset *token.FileSet, pkg *ast.Package, importer Importer) os.E
|
||||||
//
|
//
|
||||||
func CheckFile(fset *token.FileSet, file *ast.File, importer Importer) os.Error {
|
func CheckFile(fset *token.FileSet, file *ast.File, importer Importer) os.Error {
|
||||||
// create a single-file dummy package
|
// create a single-file dummy package
|
||||||
pkg := &ast.Package{file.Name.Name, nil, map[string]*ast.File{fset.Position(file.Name.NamePos).Filename: file}}
|
pkg := &ast.Package{file.Name.Name, nil, nil, map[string]*ast.File{fset.Position(file.Name.NamePos).Filename: file}}
|
||||||
return CheckPackage(fset, pkg, importer)
|
return CheckPackage(fset, pkg, importer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,8 +327,8 @@ func (tc *typechecker) checkBlock(body []ast.Stmt, ftype *Type) {
|
||||||
if ftype != nil {
|
if ftype != nil {
|
||||||
for _, par := range ftype.Params.Objects {
|
for _, par := range ftype.Params.Objects {
|
||||||
if par.Name != "_" {
|
if par.Name != "_" {
|
||||||
obj := tc.topScope.Insert(par)
|
alt := tc.topScope.Insert(par)
|
||||||
assert(obj == par) // ftype has no double declarations
|
assert(alt == nil) // ftype has no double declarations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ func expectedErrors(t *testing.T, pkg *ast.Package) (list scanner.ErrorList) {
|
||||||
case token.EOF:
|
case token.EOF:
|
||||||
break loop
|
break loop
|
||||||
case token.COMMENT:
|
case token.COMMENT:
|
||||||
s := errRx.FindSubmatch(lit)
|
s := errRx.FindStringSubmatch(lit)
|
||||||
if len(s) == 2 {
|
if len(s) == 2 {
|
||||||
list = append(list, &scanner.Error{fset.Position(prev), string(s[1])})
|
list = append(list, &scanner.Error{fset.Position(prev), string(s[1])})
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ var Universe *ast.Scope
|
||||||
|
|
||||||
func def(obj *ast.Object) {
|
func def(obj *ast.Object) {
|
||||||
alt := Universe.Insert(obj)
|
alt := Universe.Insert(obj)
|
||||||
if alt != obj {
|
if alt != nil {
|
||||||
panic("object declared twice")
|
panic("object declared twice")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -407,7 +407,7 @@ func (s *structType) string() string { return s.safeString(make(map[typeId]bool)
|
||||||
func newStructType(name string) *structType {
|
func newStructType(name string) *structType {
|
||||||
s := &structType{CommonType{Name: name}, nil}
|
s := &structType{CommonType{Name: name}, nil}
|
||||||
// For historical reasons we set the id here rather than init.
|
// For historical reasons we set the id here rather than init.
|
||||||
// Se the comment in newTypeObject for details.
|
// See the comment in newTypeObject for details.
|
||||||
setTypeId(s)
|
setTypeId(s)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -545,7 +545,7 @@ func getBaseType(name string, rt reflect.Type) (gobType, os.Error) {
|
||||||
// getType returns the Gob type describing the given reflect.Type.
|
// getType returns the Gob type describing the given reflect.Type.
|
||||||
// Should be called only when handling GobEncoders/Decoders,
|
// Should be called only when handling GobEncoders/Decoders,
|
||||||
// which may be pointers. All other types are handled through the
|
// which may be pointers. All other types are handled through the
|
||||||
// base type, never a pointer.
|
// base type, never a pointer.
|
||||||
// typeLock must be held.
|
// typeLock must be held.
|
||||||
func getType(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error) {
|
func getType(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error) {
|
||||||
typ, present := types[rt]
|
typ, present := types[rt]
|
||||||
|
@ -561,7 +561,7 @@ func getType(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error)
|
||||||
|
|
||||||
func checkId(want, got typeId) {
|
func checkId(want, got typeId) {
|
||||||
if want != got {
|
if want != got {
|
||||||
fmt.Fprintf(os.Stderr, "checkId: %d should be %d\n", int(want), int(got))
|
fmt.Fprintf(os.Stderr, "checkId: %d should be %d\n", int(got), int(want))
|
||||||
panic("bootstrap type wrong id: " + got.name() + " " + got.string() + " not " + want.string())
|
panic("bootstrap type wrong id: " + got.name() + " " + got.string() + " not " + want.string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,7 +154,10 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) {
|
||||||
// handle Content-Range header.
|
// handle Content-Range header.
|
||||||
// TODO(adg): handle multiple ranges
|
// TODO(adg): handle multiple ranges
|
||||||
ranges, err := parseRange(r.Header.Get("Range"), size)
|
ranges, err := parseRange(r.Header.Get("Range"), size)
|
||||||
if err != nil || len(ranges) > 1 {
|
if err == nil && len(ranges) > 1 {
|
||||||
|
err = os.ErrorString("multiple ranges not supported")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
Error(w, err.String(), StatusRequestedRangeNotSatisfiable)
|
Error(w, err.String(), StatusRequestedRangeNotSatisfiable)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
//
|
//
|
||||||
// pprof http://localhost:6060/debug/pprof/heap
|
// pprof http://localhost:6060/debug/pprof/heap
|
||||||
//
|
//
|
||||||
|
// Or to look at a 30-second CPU profile:
|
||||||
|
//
|
||||||
|
// pprof http://localhost:6060/debug/pprof/profile
|
||||||
|
//
|
||||||
package pprof
|
package pprof
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -29,10 +33,12 @@ import (
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
http.Handle("/debug/pprof/cmdline", http.HandlerFunc(Cmdline))
|
http.Handle("/debug/pprof/cmdline", http.HandlerFunc(Cmdline))
|
||||||
|
http.Handle("/debug/pprof/profile", http.HandlerFunc(Profile))
|
||||||
http.Handle("/debug/pprof/heap", http.HandlerFunc(Heap))
|
http.Handle("/debug/pprof/heap", http.HandlerFunc(Heap))
|
||||||
http.Handle("/debug/pprof/symbol", http.HandlerFunc(Symbol))
|
http.Handle("/debug/pprof/symbol", http.HandlerFunc(Symbol))
|
||||||
}
|
}
|
||||||
|
@ -41,22 +47,46 @@ func init() {
|
||||||
// command line, with arguments separated by NUL bytes.
|
// command line, with arguments separated by NUL bytes.
|
||||||
// The package initialization registers it as /debug/pprof/cmdline.
|
// The package initialization registers it as /debug/pprof/cmdline.
|
||||||
func Cmdline(w http.ResponseWriter, r *http.Request) {
|
func Cmdline(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("content-type", "text/plain; charset=utf-8")
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||||
fmt.Fprintf(w, strings.Join(os.Args, "\x00"))
|
fmt.Fprintf(w, strings.Join(os.Args, "\x00"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Heap responds with the pprof-formatted heap profile.
|
// Heap responds with the pprof-formatted heap profile.
|
||||||
// The package initialization registers it as /debug/pprof/heap.
|
// The package initialization registers it as /debug/pprof/heap.
|
||||||
func Heap(w http.ResponseWriter, r *http.Request) {
|
func Heap(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("content-type", "text/plain; charset=utf-8")
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||||
pprof.WriteHeapProfile(w)
|
pprof.WriteHeapProfile(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Profile responds with the pprof-formatted cpu profile.
|
||||||
|
// The package initialization registers it as /debug/pprof/profile.
|
||||||
|
func Profile(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sec, _ := strconv.Atoi64(r.FormValue("seconds"))
|
||||||
|
if sec == 0 {
|
||||||
|
sec = 30
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Content Type assuming StartCPUProfile will work,
|
||||||
|
// because if it does it starts writing.
|
||||||
|
w.Header().Set("Content-Type", "application/octet-stream")
|
||||||
|
if err := pprof.StartCPUProfile(w); err != nil {
|
||||||
|
// StartCPUProfile failed, so no writes yet.
|
||||||
|
// Can change header back to text content
|
||||||
|
// and send error code.
|
||||||
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
fmt.Fprintf(w, "Could not enable CPU profiling: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(sec * 1e9)
|
||||||
|
pprof.StopCPUProfile()
|
||||||
|
}
|
||||||
|
|
||||||
// Symbol looks up the program counters listed in the request,
|
// Symbol looks up the program counters listed in the request,
|
||||||
// responding with a table mapping program counters to function names.
|
// responding with a table mapping program counters to function names.
|
||||||
// The package initialization registers it as /debug/pprof/symbol.
|
// The package initialization registers it as /debug/pprof/symbol.
|
||||||
func Symbol(w http.ResponseWriter, r *http.Request) {
|
func Symbol(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("content-type", "text/plain; charset=utf-8")
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||||
|
|
||||||
// We don't know how many symbols we have, but we
|
// We don't know how many symbols we have, but we
|
||||||
// do have symbol information. Pprof only cares whether
|
// do have symbol information. Pprof only cares whether
|
||||||
|
|
|
@ -175,7 +175,7 @@ func TestHostHandlers(t *testing.T) {
|
||||||
ts := httptest.NewServer(nil)
|
ts := httptest.NewServer(nil)
|
||||||
defer ts.Close()
|
defer ts.Close()
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "", ts.Listener.Addr().String())
|
conn, err := net.Dial("tcp", ts.Listener.Addr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -265,7 +265,7 @@ func TestServerTimeouts(t *testing.T) {
|
||||||
|
|
||||||
// Slow client that should timeout.
|
// Slow client that should timeout.
|
||||||
t1 := time.Nanoseconds()
|
t1 := time.Nanoseconds()
|
||||||
conn, err := net.Dial("tcp", "", fmt.Sprintf("localhost:%d", addr.Port))
|
conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", addr.Port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Dial: %v", err)
|
t.Fatalf("Dial: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -348,7 +348,7 @@ func TestIdentityResponse(t *testing.T) {
|
||||||
}
|
}
|
||||||
// Verify that the connection is closed when the declared Content-Length
|
// Verify that the connection is closed when the declared Content-Length
|
||||||
// is larger than what the handler wrote.
|
// is larger than what the handler wrote.
|
||||||
conn, err := net.Dial("tcp", "", ts.Listener.Addr().String())
|
conn, err := net.Dial("tcp", ts.Listener.Addr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error dialing: %v", err)
|
t.Fatalf("error dialing: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -377,7 +377,7 @@ func TestServeHTTP10Close(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "", s.Listener.Addr().String())
|
conn, err := net.Dial("tcp", s.Listener.Addr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("dial error:", err)
|
t.Fatal("dial error:", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,7 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, os.Error) {
|
||||||
return pc, nil
|
return pc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "", cm.addr())
|
conn, err := net.Dial("tcp", cm.addr())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,12 @@ var filenames = []string{
|
||||||
"basn6a16",
|
"basn6a16",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var filenamesShort = []string{
|
||||||
|
"basn0g01",
|
||||||
|
"basn0g04-31",
|
||||||
|
"basn6a16",
|
||||||
|
}
|
||||||
|
|
||||||
func readPng(filename string) (image.Image, os.Error) {
|
func readPng(filename string) (image.Image, os.Error) {
|
||||||
f, err := os.Open(filename, os.O_RDONLY, 0444)
|
f, err := os.Open(filename, os.O_RDONLY, 0444)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -157,7 +163,11 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReader(t *testing.T) {
|
func TestReader(t *testing.T) {
|
||||||
for _, fn := range filenames {
|
names := filenames
|
||||||
|
if testing.Short() {
|
||||||
|
names = filenamesShort
|
||||||
|
}
|
||||||
|
for _, fn := range names {
|
||||||
// Read the .png file.
|
// Read the .png file.
|
||||||
img, err := readPng("testdata/pngsuite/" + fn + ".png")
|
img, err := readPng("testdata/pngsuite/" + fn + ".png")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -32,7 +32,11 @@ func diff(m0, m1 image.Image) os.Error {
|
||||||
|
|
||||||
func TestWriter(t *testing.T) {
|
func TestWriter(t *testing.T) {
|
||||||
// The filenames variable is declared in reader_test.go.
|
// The filenames variable is declared in reader_test.go.
|
||||||
for _, fn := range filenames {
|
names := filenames
|
||||||
|
if testing.Short() {
|
||||||
|
names = filenamesShort
|
||||||
|
}
|
||||||
|
for _, fn := range names {
|
||||||
qfn := "testdata/pngsuite/" + fn + ".png"
|
qfn := "testdata/pngsuite/" + fn + ".png"
|
||||||
// Read the image.
|
// Read the image.
|
||||||
m0, err := readPng(qfn)
|
m0, err := readPng(qfn)
|
||||||
|
|
|
@ -157,6 +157,7 @@ func TestUnmarshal(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalMarshal(t *testing.T) {
|
func TestUnmarshalMarshal(t *testing.T) {
|
||||||
|
initBig()
|
||||||
var v interface{}
|
var v interface{}
|
||||||
if err := Unmarshal(jsonBig, &v); err != nil {
|
if err := Unmarshal(jsonBig, &v); err != nil {
|
||||||
t.Fatalf("Unmarshal: %v", err)
|
t.Fatalf("Unmarshal: %v", err)
|
||||||
|
|
|
@ -85,6 +85,7 @@ func TestIndent(t *testing.T) {
|
||||||
// Tests of a large random structure.
|
// Tests of a large random structure.
|
||||||
|
|
||||||
func TestCompactBig(t *testing.T) {
|
func TestCompactBig(t *testing.T) {
|
||||||
|
initBig()
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if err := Compact(&buf, jsonBig); err != nil {
|
if err := Compact(&buf, jsonBig); err != nil {
|
||||||
t.Fatalf("Compact: %v", err)
|
t.Fatalf("Compact: %v", err)
|
||||||
|
@ -98,6 +99,7 @@ func TestCompactBig(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIndentBig(t *testing.T) {
|
func TestIndentBig(t *testing.T) {
|
||||||
|
initBig()
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if err := Indent(&buf, jsonBig, "", "\t"); err != nil {
|
if err := Indent(&buf, jsonBig, "", "\t"); err != nil {
|
||||||
t.Fatalf("Indent1: %v", err)
|
t.Fatalf("Indent1: %v", err)
|
||||||
|
@ -135,6 +137,7 @@ func TestIndentBig(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNextValueBig(t *testing.T) {
|
func TestNextValueBig(t *testing.T) {
|
||||||
|
initBig()
|
||||||
var scan scanner
|
var scan scanner
|
||||||
item, rest, err := nextValue(jsonBig, &scan)
|
item, rest, err := nextValue(jsonBig, &scan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -160,6 +163,7 @@ func TestNextValueBig(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkSkipValue(b *testing.B) {
|
func BenchmarkSkipValue(b *testing.B) {
|
||||||
|
initBig()
|
||||||
var scan scanner
|
var scan scanner
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
nextValue(jsonBig, &scan)
|
nextValue(jsonBig, &scan)
|
||||||
|
@ -191,12 +195,23 @@ func trim(b []byte) []byte {
|
||||||
|
|
||||||
var jsonBig []byte
|
var jsonBig []byte
|
||||||
|
|
||||||
func init() {
|
const (
|
||||||
b, err := Marshal(genValue(10000))
|
big = 10000
|
||||||
if err != nil {
|
small = 100
|
||||||
panic(err)
|
)
|
||||||
|
|
||||||
|
func initBig() {
|
||||||
|
n := big
|
||||||
|
if testing.Short() {
|
||||||
|
n = small
|
||||||
|
}
|
||||||
|
if len(jsonBig) != n {
|
||||||
|
b, err := Marshal(genValue(n))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
jsonBig = b
|
||||||
}
|
}
|
||||||
jsonBig = b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func genValue(n int) interface{} {
|
func genValue(n int) interface{} {
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2011 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.
|
||||||
|
|
||||||
|
// Stub cgo routines for systems that do not use cgo to do network lookups.
|
||||||
|
|
||||||
|
package net
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func cgoLookupHost(name string) (addrs []string, err os.Error, completed bool) {
|
||||||
|
return nil, nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func cgoLookupPort(network, service string) (port int, err os.Error, completed bool) {
|
||||||
|
return 0, nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func cgoLookupIP(name string) (addrs []IP, err os.Error, completed bool) {
|
||||||
|
return nil, nil, false
|
||||||
|
}
|
|
@ -6,9 +6,7 @@ package net
|
||||||
|
|
||||||
import "os"
|
import "os"
|
||||||
|
|
||||||
// Dial connects to the remote address raddr on the network net.
|
// Dial connects to the address addr on the network net.
|
||||||
// If the string laddr is not empty, it is used as the local address
|
|
||||||
// for the connection.
|
|
||||||
//
|
//
|
||||||
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
|
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
|
||||||
// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
|
// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
|
||||||
|
@ -16,79 +14,56 @@ import "os"
|
||||||
//
|
//
|
||||||
// For IP networks, addresses have the form host:port. If host is
|
// For IP networks, addresses have the form host:port. If host is
|
||||||
// a literal IPv6 address, it must be enclosed in square brackets.
|
// a literal IPv6 address, it must be enclosed in square brackets.
|
||||||
|
// The functions JoinHostPort and SplitHostPort manipulate
|
||||||
|
// addresses in this form.
|
||||||
//
|
//
|
||||||
// Examples:
|
// Examples:
|
||||||
// Dial("tcp", "", "12.34.56.78:80")
|
// Dial("tcp", "12.34.56.78:80")
|
||||||
// Dial("tcp", "", "google.com:80")
|
// Dial("tcp", "google.com:80")
|
||||||
// Dial("tcp", "", "[de:ad:be:ef::ca:fe]:80")
|
// Dial("tcp", "[de:ad:be:ef::ca:fe]:80")
|
||||||
// Dial("tcp", "127.0.0.1:123", "127.0.0.1:88")
|
|
||||||
//
|
//
|
||||||
func Dial(net, laddr, raddr string) (c Conn, err os.Error) {
|
func Dial(net, addr string) (c Conn, err os.Error) {
|
||||||
|
raddr := addr
|
||||||
|
if raddr == "" {
|
||||||
|
return nil, &OpError{"dial", net, nil, errMissingAddress}
|
||||||
|
}
|
||||||
switch net {
|
switch net {
|
||||||
case "tcp", "tcp4", "tcp6":
|
case "tcp", "tcp4", "tcp6":
|
||||||
var la, ra *TCPAddr
|
var ra *TCPAddr
|
||||||
if laddr != "" {
|
if ra, err = ResolveTCPAddr(raddr); err != nil {
|
||||||
if la, err = ResolveTCPAddr(laddr); err != nil {
|
goto Error
|
||||||
goto Error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if raddr != "" {
|
c, err := DialTCP(net, nil, ra)
|
||||||
if ra, err = ResolveTCPAddr(raddr); err != nil {
|
|
||||||
goto Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c, err := DialTCP(net, la, ra)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
case "udp", "udp4", "udp6":
|
case "udp", "udp4", "udp6":
|
||||||
var la, ra *UDPAddr
|
var ra *UDPAddr
|
||||||
if laddr != "" {
|
if ra, err = ResolveUDPAddr(raddr); err != nil {
|
||||||
if la, err = ResolveUDPAddr(laddr); err != nil {
|
goto Error
|
||||||
goto Error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if raddr != "" {
|
c, err := DialUDP(net, nil, ra)
|
||||||
if ra, err = ResolveUDPAddr(raddr); err != nil {
|
|
||||||
goto Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c, err := DialUDP(net, la, ra)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
case "unix", "unixgram", "unixpacket":
|
case "unix", "unixgram", "unixpacket":
|
||||||
var la, ra *UnixAddr
|
var ra *UnixAddr
|
||||||
if raddr != "" {
|
if ra, err = ResolveUnixAddr(net, raddr); err != nil {
|
||||||
if ra, err = ResolveUnixAddr(net, raddr); err != nil {
|
goto Error
|
||||||
goto Error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if laddr != "" {
|
c, err = DialUnix(net, nil, ra)
|
||||||
if la, err = ResolveUnixAddr(net, laddr); err != nil {
|
|
||||||
goto Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c, err = DialUnix(net, la, ra)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
case "ip", "ip4", "ip6":
|
case "ip", "ip4", "ip6":
|
||||||
var la, ra *IPAddr
|
var ra *IPAddr
|
||||||
if laddr != "" {
|
if ra, err = ResolveIPAddr(raddr); err != nil {
|
||||||
if la, err = ResolveIPAddr(laddr); err != nil {
|
goto Error
|
||||||
goto Error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if raddr != "" {
|
c, err := DialIP(net, nil, ra)
|
||||||
if ra, err = ResolveIPAddr(raddr); err != nil {
|
|
||||||
goto Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c, err := DialIP(net, la, ra)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ func fetchGoogle(t *testing.T, fd Conn, network, addr string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func doDial(t *testing.T, network, addr string) {
|
func doDial(t *testing.T, network, addr string) {
|
||||||
fd, err := Dial(network, "", addr)
|
fd, err := Dial(network, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Dial(%q, %q, %q) = _, %v", network, "", addr, err)
|
t.Errorf("Dial(%q, %q, %q) = _, %v", network, "", addr, err)
|
||||||
return
|
return
|
||||||
|
@ -55,6 +55,13 @@ var googleaddrs = []string{
|
||||||
"[2001:4860:0:2001::68]:80", // ipv6.google.com; removed if ipv6 flag not set
|
"[2001:4860:0:2001::68]:80", // ipv6.google.com; removed if ipv6 flag not set
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLookupCNAME(t *testing.T) {
|
||||||
|
cname, err := LookupCNAME("www.google.com")
|
||||||
|
if cname != "www.l.google.com." || err != nil {
|
||||||
|
t.Errorf(`LookupCNAME("www.google.com.") = %q, %v, want "www.l.google.com.", nil`, cname, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDialGoogle(t *testing.T) {
|
func TestDialGoogle(t *testing.T) {
|
||||||
// If no ipv6 tunnel, don't try the last address.
|
// If no ipv6 tunnel, don't try the last address.
|
||||||
if !*ipv6 {
|
if !*ipv6 {
|
||||||
|
@ -64,14 +71,14 @@ func TestDialGoogle(t *testing.T) {
|
||||||
// Insert an actual IP address for google.com
|
// Insert an actual IP address for google.com
|
||||||
// into the table.
|
// into the table.
|
||||||
|
|
||||||
_, addrs, err := LookupHost("www.google.com")
|
addrs, err := LookupIP("www.google.com")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("lookup www.google.com: %v", err)
|
t.Fatalf("lookup www.google.com: %v", err)
|
||||||
}
|
}
|
||||||
if len(addrs) == 0 {
|
if len(addrs) == 0 {
|
||||||
t.Fatalf("no addresses for www.google.com")
|
t.Fatalf("no addresses for www.google.com")
|
||||||
}
|
}
|
||||||
ip := ParseIP(addrs[0]).To4()
|
ip := addrs[0].To4()
|
||||||
|
|
||||||
for i, s := range googleaddrs {
|
for i, s := range googleaddrs {
|
||||||
if strings.Contains(s, "%") {
|
if strings.Contains(s, "%") {
|
||||||
|
|
|
@ -159,7 +159,7 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs
|
||||||
// all the cfg.servers[i] are IP addresses, which
|
// all the cfg.servers[i] are IP addresses, which
|
||||||
// Dial will use without a DNS lookup.
|
// Dial will use without a DNS lookup.
|
||||||
server := cfg.servers[i] + ":53"
|
server := cfg.servers[i] + ":53"
|
||||||
c, cerr := Dial("udp", "", server)
|
c, cerr := Dial("udp", server)
|
||||||
if cerr != nil {
|
if cerr != nil {
|
||||||
err = cerr
|
err = cerr
|
||||||
continue
|
continue
|
||||||
|
@ -178,12 +178,23 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertRR_A(records []dnsRR) []string {
|
func convertRR_A(records []dnsRR) []IP {
|
||||||
addrs := make([]string, len(records))
|
addrs := make([]IP, len(records))
|
||||||
for i := 0; i < len(records); i++ {
|
for i := 0; i < len(records); i++ {
|
||||||
rr := records[i]
|
rr := records[i]
|
||||||
a := rr.(*dnsRR_A).A
|
a := rr.(*dnsRR_A).A
|
||||||
addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)).String()
|
addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
|
||||||
|
}
|
||||||
|
return addrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertRR_AAAA(records []dnsRR) []IP {
|
||||||
|
addrs := make([]IP, len(records))
|
||||||
|
for i := 0; i < len(records); i++ {
|
||||||
|
rr := records[i]
|
||||||
|
a := make(IP, 16)
|
||||||
|
copy(a, rr.(*dnsRR_AAAA).AAAA[:])
|
||||||
|
addrs[i] = a
|
||||||
}
|
}
|
||||||
return addrs
|
return addrs
|
||||||
}
|
}
|
||||||
|
@ -294,10 +305,8 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Erro
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupHost looks for name using the local hosts file and DNS resolver.
|
// goLookupHost is the native Go implementation of LookupHost.
|
||||||
// It returns the canonical name for the host and an array of that
|
func goLookupHost(name string) (addrs []string, err os.Error) {
|
||||||
// host's addresses.
|
|
||||||
func LookupHost(name string) (cname string, addrs []string, err os.Error) {
|
|
||||||
onceLoadConfig.Do(loadConfig)
|
onceLoadConfig.Do(loadConfig)
|
||||||
if dnserr != nil || cfg == nil {
|
if dnserr != nil || cfg == nil {
|
||||||
err = dnserr
|
err = dnserr
|
||||||
|
@ -306,18 +315,69 @@ func LookupHost(name string) (cname string, addrs []string, err os.Error) {
|
||||||
// Use entries from /etc/hosts if they match.
|
// Use entries from /etc/hosts if they match.
|
||||||
addrs = lookupStaticHost(name)
|
addrs = lookupStaticHost(name)
|
||||||
if len(addrs) > 0 {
|
if len(addrs) > 0 {
|
||||||
cname = name
|
return
|
||||||
|
}
|
||||||
|
ips, err := goLookupIP(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addrs = make([]string, 0, len(ips))
|
||||||
|
for _, ip := range ips {
|
||||||
|
addrs = append(addrs, ip.String())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// goLookupIP is the native Go implementation of LookupIP.
|
||||||
|
func goLookupIP(name string) (addrs []IP, err os.Error) {
|
||||||
|
onceLoadConfig.Do(loadConfig)
|
||||||
|
if dnserr != nil || cfg == nil {
|
||||||
|
err = dnserr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var records []dnsRR
|
var records []dnsRR
|
||||||
|
var cname string
|
||||||
cname, records, err = lookup(name, dnsTypeA)
|
cname, records, err = lookup(name, dnsTypeA)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
addrs = convertRR_A(records)
|
addrs = convertRR_A(records)
|
||||||
|
if cname != "" {
|
||||||
|
name = cname
|
||||||
|
}
|
||||||
|
_, records, err = lookup(name, dnsTypeAAAA)
|
||||||
|
if err != nil && len(addrs) > 0 {
|
||||||
|
// Ignore error because A lookup succeeded.
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addrs = append(addrs, convertRR_AAAA(records)...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LookupCNAME returns the canonical DNS host for the given name.
|
||||||
|
// Callers that do not care about the canonical name can call
|
||||||
|
// LookupHost or LookupIP directly; both take care of resolving
|
||||||
|
// the canonical name as part of the lookup.
|
||||||
|
func LookupCNAME(name string) (cname string, err os.Error) {
|
||||||
|
onceLoadConfig.Do(loadConfig)
|
||||||
|
if dnserr != nil || cfg == nil {
|
||||||
|
err = dnserr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, rr, err := lookup(name, dnsTypeCNAME)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(rr) >= 0 {
|
||||||
|
cname = rr[0].(*dnsRR_CNAME).Cname
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// An SRV represents a single DNS SRV record.
|
||||||
type SRV struct {
|
type SRV struct {
|
||||||
Target string
|
Target string
|
||||||
Port uint16
|
Port uint16
|
||||||
|
@ -344,11 +404,13 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An MX represents a single DNS MX record.
|
||||||
type MX struct {
|
type MX struct {
|
||||||
Host string
|
Host string
|
||||||
Pref uint16
|
Pref uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LookupMX returns the DNS MX records associated with name.
|
||||||
func LookupMX(name string) (entries []*MX, err os.Error) {
|
func LookupMX(name string) (entries []*MX, err os.Error) {
|
||||||
var records []dnsRR
|
var records []dnsRR
|
||||||
_, records, err = lookup(name, dnsTypeMX)
|
_, records, err = lookup(name, dnsTypeMX)
|
||||||
|
|
|
@ -50,6 +50,7 @@ const (
|
||||||
dnsTypeMINFO = 14
|
dnsTypeMINFO = 14
|
||||||
dnsTypeMX = 15
|
dnsTypeMX = 15
|
||||||
dnsTypeTXT = 16
|
dnsTypeTXT = 16
|
||||||
|
dnsTypeAAAA = 28
|
||||||
dnsTypeSRV = 33
|
dnsTypeSRV = 33
|
||||||
|
|
||||||
// valid dnsQuestion.qtype only
|
// valid dnsQuestion.qtype only
|
||||||
|
@ -244,8 +245,18 @@ type dnsRR_A struct {
|
||||||
A uint32 "ipv4"
|
A uint32 "ipv4"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *dnsRR_A) Header() *dnsRR_Header { return &rr.Hdr }
|
func (rr *dnsRR_A) Header() *dnsRR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
type dnsRR_AAAA struct {
|
||||||
|
Hdr dnsRR_Header
|
||||||
|
AAAA [16]byte "ipv6"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *dnsRR_AAAA) Header() *dnsRR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
// Packing and unpacking.
|
// Packing and unpacking.
|
||||||
//
|
//
|
||||||
|
@ -270,6 +281,7 @@ var rr_mk = map[int]func() dnsRR{
|
||||||
dnsTypeTXT: func() dnsRR { return new(dnsRR_TXT) },
|
dnsTypeTXT: func() dnsRR { return new(dnsRR_TXT) },
|
||||||
dnsTypeSRV: func() dnsRR { return new(dnsRR_SRV) },
|
dnsTypeSRV: func() dnsRR { return new(dnsRR_SRV) },
|
||||||
dnsTypeA: func() dnsRR { return new(dnsRR_A) },
|
dnsTypeA: func() dnsRR { return new(dnsRR_A) },
|
||||||
|
dnsTypeAAAA: func() dnsRR { return new(dnsRR_AAAA) },
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pack a domain name s into msg[off:].
|
// Pack a domain name s into msg[off:].
|
||||||
|
@ -377,7 +389,7 @@ Loop:
|
||||||
|
|
||||||
// TODO(rsc): Move into generic library?
|
// TODO(rsc): Move into generic library?
|
||||||
// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string,
|
// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string,
|
||||||
// and other (often anonymous) structs.
|
// [n]byte, and other (often anonymous) structs.
|
||||||
func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) {
|
func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) {
|
||||||
for i := 0; i < val.NumField(); i++ {
|
for i := 0; i < val.NumField(); i++ {
|
||||||
f := val.Type().(*reflect.StructType).Field(i)
|
f := val.Type().(*reflect.StructType).Field(i)
|
||||||
|
@ -410,6 +422,16 @@ func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, o
|
||||||
msg[off+3] = byte(i)
|
msg[off+3] = byte(i)
|
||||||
off += 4
|
off += 4
|
||||||
}
|
}
|
||||||
|
case *reflect.ArrayValue:
|
||||||
|
if fv.Type().(*reflect.ArrayType).Elem().Kind() != reflect.Uint8 {
|
||||||
|
goto BadType
|
||||||
|
}
|
||||||
|
n := fv.Len()
|
||||||
|
if off+n > len(msg) {
|
||||||
|
return len(msg), false
|
||||||
|
}
|
||||||
|
reflect.Copy(reflect.NewValue(msg[off:off+n]).(*reflect.SliceValue), fv)
|
||||||
|
off += n
|
||||||
case *reflect.StringValue:
|
case *reflect.StringValue:
|
||||||
// There are multiple string encodings.
|
// There are multiple string encodings.
|
||||||
// The tag distinguishes ordinary strings from domain names.
|
// The tag distinguishes ordinary strings from domain names.
|
||||||
|
@ -478,6 +500,16 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int,
|
||||||
fv.Set(uint64(i))
|
fv.Set(uint64(i))
|
||||||
off += 4
|
off += 4
|
||||||
}
|
}
|
||||||
|
case *reflect.ArrayValue:
|
||||||
|
if fv.Type().(*reflect.ArrayType).Elem().Kind() != reflect.Uint8 {
|
||||||
|
goto BadType
|
||||||
|
}
|
||||||
|
n := fv.Len()
|
||||||
|
if off+n > len(msg) {
|
||||||
|
return len(msg), false
|
||||||
|
}
|
||||||
|
reflect.Copy(fv, reflect.NewValue(msg[off:off+n]).(*reflect.SliceValue))
|
||||||
|
off += n
|
||||||
case *reflect.StringValue:
|
case *reflect.StringValue:
|
||||||
var s string
|
var s string
|
||||||
switch f.Tag {
|
switch f.Tag {
|
||||||
|
@ -515,7 +547,8 @@ func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
|
||||||
|
|
||||||
// Generic struct printer.
|
// Generic struct printer.
|
||||||
// Doesn't care about the string tag "domain-name",
|
// Doesn't care about the string tag "domain-name",
|
||||||
// but does look for an "ipv4" tag on uint32 variables,
|
// but does look for an "ipv4" tag on uint32 variables
|
||||||
|
// and the "ipv6" tag on array variables,
|
||||||
// printing them as IP addresses.
|
// printing them as IP addresses.
|
||||||
func printStructValue(val *reflect.StructValue) string {
|
func printStructValue(val *reflect.StructValue) string {
|
||||||
s := "{"
|
s := "{"
|
||||||
|
@ -533,6 +566,9 @@ func printStructValue(val *reflect.StructValue) string {
|
||||||
} else if fv, ok := fval.(*reflect.UintValue); ok && f.Tag == "ipv4" {
|
} else if fv, ok := fval.(*reflect.UintValue); ok && f.Tag == "ipv4" {
|
||||||
i := fv.Get()
|
i := fv.Get()
|
||||||
s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String()
|
s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String()
|
||||||
|
} else if fv, ok := fval.(*reflect.ArrayValue); ok && f.Tag == "ipv6" {
|
||||||
|
i := fv.Interface().([]byte)
|
||||||
|
s += IP(i).String()
|
||||||
} else {
|
} else {
|
||||||
s += fmt.Sprint(fval.Interface())
|
s += fmt.Sprint(fval.Interface())
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,19 +274,25 @@ func startServer() {
|
||||||
pollserver = p
|
pollserver = p
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err os.Error) {
|
func newFD(fd, family, proto int, net string) (f *netFD, err os.Error) {
|
||||||
onceStartServer.Do(startServer)
|
onceStartServer.Do(startServer)
|
||||||
if e := syscall.SetNonblock(fd, true); e != 0 {
|
if e := syscall.SetNonblock(fd, true); e != 0 {
|
||||||
return nil, &OpError{"setnonblock", net, laddr, os.Errno(e)}
|
return nil, os.Errno(e)
|
||||||
}
|
}
|
||||||
f = &netFD{
|
f = &netFD{
|
||||||
sysfd: fd,
|
sysfd: fd,
|
||||||
family: family,
|
family: family,
|
||||||
proto: proto,
|
proto: proto,
|
||||||
net: net,
|
net: net,
|
||||||
laddr: laddr,
|
|
||||||
raddr: raddr,
|
|
||||||
}
|
}
|
||||||
|
f.cr = make(chan bool, 1)
|
||||||
|
f.cw = make(chan bool, 1)
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fd *netFD) setAddr(laddr, raddr Addr) {
|
||||||
|
fd.laddr = laddr
|
||||||
|
fd.raddr = raddr
|
||||||
var ls, rs string
|
var ls, rs string
|
||||||
if laddr != nil {
|
if laddr != nil {
|
||||||
ls = laddr.String()
|
ls = laddr.String()
|
||||||
|
@ -294,10 +300,23 @@ func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err
|
||||||
if raddr != nil {
|
if raddr != nil {
|
||||||
rs = raddr.String()
|
rs = raddr.String()
|
||||||
}
|
}
|
||||||
f.sysfile = os.NewFile(fd, net+":"+ls+"->"+rs)
|
fd.sysfile = os.NewFile(fd.sysfd, fd.net+":"+ls+"->"+rs)
|
||||||
f.cr = make(chan bool, 1)
|
}
|
||||||
f.cw = make(chan bool, 1)
|
|
||||||
return f, nil
|
func (fd *netFD) connect(ra syscall.Sockaddr) (err os.Error) {
|
||||||
|
e := syscall.Connect(fd.sysfd, ra)
|
||||||
|
if e == syscall.EINPROGRESS {
|
||||||
|
var errno int
|
||||||
|
pollserver.WaitWrite(fd)
|
||||||
|
e, errno = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
|
||||||
|
if errno != 0 {
|
||||||
|
return os.NewSyscallError("getsockopt", errno)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if e != 0 {
|
||||||
|
return os.Errno(e)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a reference to this fd.
|
// Add a reference to this fd.
|
||||||
|
@ -593,10 +612,11 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.
|
||||||
syscall.CloseOnExec(s)
|
syscall.CloseOnExec(s)
|
||||||
syscall.ForkLock.RUnlock()
|
syscall.ForkLock.RUnlock()
|
||||||
|
|
||||||
if nfd, err = newFD(s, fd.family, fd.proto, fd.net, fd.laddr, toAddr(sa)); err != nil {
|
if nfd, err = newFD(s, fd.family, fd.proto, fd.net); err != nil {
|
||||||
syscall.Close(s)
|
syscall.Close(s)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
nfd.setAddr(fd.laddr, toAddr(sa))
|
||||||
return nfd, nil
|
return nfd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -225,29 +225,40 @@ type netFD struct {
|
||||||
wio sync.Mutex
|
wio sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func allocFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD) {
|
func allocFD(fd, family, proto int, net string) (f *netFD) {
|
||||||
f = &netFD{
|
f = &netFD{
|
||||||
sysfd: fd,
|
sysfd: fd,
|
||||||
family: family,
|
family: family,
|
||||||
proto: proto,
|
proto: proto,
|
||||||
net: net,
|
net: net,
|
||||||
laddr: laddr,
|
|
||||||
raddr: raddr,
|
|
||||||
}
|
}
|
||||||
runtime.SetFinalizer(f, (*netFD).Close)
|
runtime.SetFinalizer(f, (*netFD).Close)
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err os.Error) {
|
func newFD(fd, family, proto int, net string) (f *netFD, err os.Error) {
|
||||||
if initErr != nil {
|
if initErr != nil {
|
||||||
return nil, initErr
|
return nil, initErr
|
||||||
}
|
}
|
||||||
onceStartServer.Do(startServer)
|
onceStartServer.Do(startServer)
|
||||||
// Associate our socket with resultsrv.iocp.
|
// Associate our socket with resultsrv.iocp.
|
||||||
if _, e := syscall.CreateIoCompletionPort(int32(fd), resultsrv.iocp, 0, 0); e != 0 {
|
if _, e := syscall.CreateIoCompletionPort(int32(fd), resultsrv.iocp, 0, 0); e != 0 {
|
||||||
return nil, &OpError{"CreateIoCompletionPort", net, laddr, os.Errno(e)}
|
return nil, os.Errno(e)
|
||||||
}
|
}
|
||||||
return allocFD(fd, family, proto, net, laddr, raddr), nil
|
return allocFD(fd, family, proto, net), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fd *netFD) setAddr(laddr, raddr Addr) {
|
||||||
|
fd.laddr = laddr
|
||||||
|
fd.raddr = raddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fd *netFD) connect(ra syscall.Sockaddr) (err os.Error) {
|
||||||
|
e := syscall.Connect(fd.sysfd, ra)
|
||||||
|
if e != 0 {
|
||||||
|
return os.Errno(e)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a reference to this fd.
|
// Add a reference to this fd.
|
||||||
|
@ -497,7 +508,9 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.
|
||||||
lsa, _ := lrsa.Sockaddr()
|
lsa, _ := lrsa.Sockaddr()
|
||||||
rsa, _ := rrsa.Sockaddr()
|
rsa, _ := rrsa.Sockaddr()
|
||||||
|
|
||||||
return allocFD(s, fd.family, fd.proto, fd.net, toAddr(lsa), toAddr(rsa)), nil
|
nfd = allocFD(s, fd.family, fd.proto, fd.net)
|
||||||
|
nfd.setAddr(toAddr(lsa), toAddr(rsa))
|
||||||
|
return nfd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not implemeted functions.
|
// Not implemeted functions.
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
// Copyright 2011 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 net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newFileFD(f *os.File) (nfd *netFD, err os.Error) {
|
||||||
|
fd, errno := syscall.Dup(f.Fd())
|
||||||
|
if errno != 0 {
|
||||||
|
return nil, os.NewSyscallError("dup", errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
proto, errno := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE)
|
||||||
|
if errno != 0 {
|
||||||
|
return nil, os.NewSyscallError("getsockopt", errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
toAddr := sockaddrToTCP
|
||||||
|
sa, _ := syscall.Getsockname(fd)
|
||||||
|
switch sa.(type) {
|
||||||
|
default:
|
||||||
|
closesocket(fd)
|
||||||
|
return nil, os.EINVAL
|
||||||
|
case *syscall.SockaddrInet4:
|
||||||
|
if proto == syscall.SOCK_DGRAM {
|
||||||
|
toAddr = sockaddrToUDP
|
||||||
|
} else if proto == syscall.SOCK_RAW {
|
||||||
|
toAddr = sockaddrToIP
|
||||||
|
}
|
||||||
|
case *syscall.SockaddrInet6:
|
||||||
|
if proto == syscall.SOCK_DGRAM {
|
||||||
|
toAddr = sockaddrToUDP
|
||||||
|
} else if proto == syscall.SOCK_RAW {
|
||||||
|
toAddr = sockaddrToIP
|
||||||
|
}
|
||||||
|
case *syscall.SockaddrUnix:
|
||||||
|
toAddr = sockaddrToUnix
|
||||||
|
if proto == syscall.SOCK_DGRAM {
|
||||||
|
toAddr = sockaddrToUnixgram
|
||||||
|
} else if proto == syscall.SOCK_SEQPACKET {
|
||||||
|
toAddr = sockaddrToUnixpacket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
laddr := toAddr(sa)
|
||||||
|
sa, _ = syscall.Getpeername(fd)
|
||||||
|
raddr := toAddr(sa)
|
||||||
|
|
||||||
|
if nfd, err = newFD(fd, 0, proto, laddr.Network()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nfd.setAddr(laddr, raddr)
|
||||||
|
return nfd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileConn returns a copy of the network connection corresponding to
|
||||||
|
// the open file f. It is the caller's responsibility to close f when
|
||||||
|
// finished. Closing c does not affect f, and closing f does not
|
||||||
|
// affect c.
|
||||||
|
func FileConn(f *os.File) (c Conn, err os.Error) {
|
||||||
|
fd, err := newFileFD(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch fd.laddr.(type) {
|
||||||
|
case *TCPAddr:
|
||||||
|
return newTCPConn(fd), nil
|
||||||
|
case *UDPAddr:
|
||||||
|
return newUDPConn(fd), nil
|
||||||
|
case *UnixAddr:
|
||||||
|
return newUnixConn(fd), nil
|
||||||
|
case *IPAddr:
|
||||||
|
return newIPConn(fd), nil
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
return nil, os.EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileListener returns a copy of the network listener corresponding
|
||||||
|
// to the open file f. It is the caller's responsibility to close l
|
||||||
|
// when finished. Closing c does not affect l, and closing l does not
|
||||||
|
// affect c.
|
||||||
|
func FileListener(f *os.File) (l Listener, err os.Error) {
|
||||||
|
fd, err := newFileFD(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch laddr := fd.laddr.(type) {
|
||||||
|
case *TCPAddr:
|
||||||
|
return &TCPListener{fd}, nil
|
||||||
|
case *UnixAddr:
|
||||||
|
return &UnixListener{fd, laddr.Name}, nil
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
return nil, os.EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilePacketConn returns a copy of the packet network connection
|
||||||
|
// corresponding to the open file f. It is the caller's
|
||||||
|
// responsibility to close f when finished. Closing c does not affect
|
||||||
|
// f, and closing f does not affect c.
|
||||||
|
func FilePacketConn(f *os.File) (c PacketConn, err os.Error) {
|
||||||
|
fd, err := newFileFD(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch fd.laddr.(type) {
|
||||||
|
case *UDPAddr:
|
||||||
|
return newUDPConn(fd), nil
|
||||||
|
case *UnixAddr:
|
||||||
|
return newUnixConn(fd), nil
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
return nil, os.EINVAL
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
// Copyright 2011 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 net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type listenerFile interface {
|
||||||
|
Listener
|
||||||
|
File() (f *os.File, err os.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type packetConnFile interface {
|
||||||
|
PacketConn
|
||||||
|
File() (f *os.File, err os.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type connFile interface {
|
||||||
|
Conn
|
||||||
|
File() (f *os.File, err os.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFileListener(t *testing.T, net, laddr string) {
|
||||||
|
if net == "tcp" {
|
||||||
|
laddr += ":0" // any available port
|
||||||
|
}
|
||||||
|
l, err := Listen(net, laddr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Listen failed: %v", err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
lf := l.(listenerFile)
|
||||||
|
f, err := lf.File()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("File failed: %v", err)
|
||||||
|
}
|
||||||
|
c, err := FileListener(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("FileListener failed: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(l.Addr(), c.Addr()) {
|
||||||
|
t.Fatalf("Addrs not equal: %#v != %#v", l.Addr(), c.Addr())
|
||||||
|
}
|
||||||
|
if err := c.Close(); err != nil {
|
||||||
|
t.Fatalf("Close failed: %v", err)
|
||||||
|
}
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
t.Fatalf("Close failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileListener(t *testing.T) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
testFileListener(t, "tcp", "127.0.0.1")
|
||||||
|
testFileListener(t, "tcp", "127.0.0.1")
|
||||||
|
if kernelSupportsIPv6() {
|
||||||
|
testFileListener(t, "tcp", "[::ffff:127.0.0.1]")
|
||||||
|
testFileListener(t, "tcp", "127.0.0.1")
|
||||||
|
testFileListener(t, "tcp", "[::ffff:127.0.0.1]")
|
||||||
|
}
|
||||||
|
if syscall.OS == "linux" {
|
||||||
|
testFileListener(t, "unix", "@gotest/net")
|
||||||
|
testFileListener(t, "unixpacket", "@gotest/net")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFilePacketConn(t *testing.T, pcf packetConnFile) {
|
||||||
|
f, err := pcf.File()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("File failed: %v", err)
|
||||||
|
}
|
||||||
|
c, err := FilePacketConn(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("FilePacketConn failed: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(pcf.LocalAddr(), c.LocalAddr()) {
|
||||||
|
t.Fatalf("LocalAddrs not equal: %#v != %#v", pcf.LocalAddr(), c.LocalAddr())
|
||||||
|
}
|
||||||
|
if err := c.Close(); err != nil {
|
||||||
|
t.Fatalf("Close failed: %v", err)
|
||||||
|
}
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
t.Fatalf("Close failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFilePacketConnListen(t *testing.T, net, laddr string) {
|
||||||
|
l, err := ListenPacket(net, laddr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Listen failed: %v", err)
|
||||||
|
}
|
||||||
|
testFilePacketConn(t, l.(packetConnFile))
|
||||||
|
if err := l.Close(); err != nil {
|
||||||
|
t.Fatalf("Close failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFilePacketConnDial(t *testing.T, net, raddr string) {
|
||||||
|
c, err := Dial(net, raddr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Dial failed: %v", err)
|
||||||
|
}
|
||||||
|
testFilePacketConn(t, c.(packetConnFile))
|
||||||
|
if err := c.Close(); err != nil {
|
||||||
|
t.Fatalf("Close failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilePacketConn(t *testing.T) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
testFilePacketConnListen(t, "udp", "127.0.0.1:0")
|
||||||
|
testFilePacketConnDial(t, "udp", "127.0.0.1:12345")
|
||||||
|
if kernelSupportsIPv6() {
|
||||||
|
testFilePacketConnListen(t, "udp", "[::1]:0")
|
||||||
|
testFilePacketConnDial(t, "udp", "[::ffff:127.0.0.1]:12345")
|
||||||
|
}
|
||||||
|
if syscall.OS == "linux" {
|
||||||
|
testFilePacketConnListen(t, "unixgram", "@gotest1/net")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2011 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 net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FileConn(f *os.File) (c Conn, err os.Error) {
|
||||||
|
// TODO: Implement this
|
||||||
|
return nil, os.NewSyscallError("FileConn", syscall.EWINDOWS)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FileListener(f *os.File) (l Listener, err os.Error) {
|
||||||
|
// TODO: Implement this
|
||||||
|
return nil, os.NewSyscallError("FileListener", syscall.EWINDOWS)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilePacketConn(f *os.File) (c PacketConn, err os.Error) {
|
||||||
|
// TODO: Implement this
|
||||||
|
return nil, os.NewSyscallError("FilePacketConn", syscall.EWINDOWS)
|
||||||
|
}
|
|
@ -13,7 +13,6 @@ type hostTest struct {
|
||||||
ips []IP
|
ips []IP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var hosttests = []hostTest{
|
var hosttests = []hostTest{
|
||||||
{"odin", []IP{
|
{"odin", []IP{
|
||||||
IPv4(127, 0, 0, 2),
|
IPv4(127, 0, 0, 2),
|
||||||
|
|
|
@ -474,13 +474,13 @@ func parseIPv6(s string) IP {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// A SyntaxError represents a malformed text string and the type of string that was expected.
|
// A ParseError represents a malformed text string and the type of string that was expected.
|
||||||
type SyntaxError struct {
|
type ParseError struct {
|
||||||
Type string
|
Type string
|
||||||
Text string
|
Text string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *SyntaxError) String() string {
|
func (e *ParseError) String() string {
|
||||||
return "invalid " + e.Type + ": " + e.Text
|
return "invalid " + e.Type + ": " + e.Text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -507,33 +507,46 @@ func ParseIP(s string) IP {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseCIDR parses s as a CIDR notation IP address and mask,
|
// ParseCIDR parses s as a CIDR notation IP address and mask,
|
||||||
// like "192.168.100.1/24" or "2001:DB8::/48".
|
// like "192.168.100.1/24", "2001:DB8::/48", as defined in
|
||||||
|
// RFC 4632 and RFC 4291.
|
||||||
func ParseCIDR(s string) (ip IP, mask IPMask, err os.Error) {
|
func ParseCIDR(s string) (ip IP, mask IPMask, err os.Error) {
|
||||||
i := byteIndex(s, '/')
|
i := byteIndex(s, '/')
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
return nil, nil, &SyntaxError{"CIDR address", s}
|
return nil, nil, &ParseError{"CIDR address", s}
|
||||||
}
|
}
|
||||||
ipstr, maskstr := s[:i], s[i+1:]
|
ipstr, maskstr := s[:i], s[i+1:]
|
||||||
ip = ParseIP(ipstr)
|
iplen := 4
|
||||||
|
ip = parseIPv4(ipstr)
|
||||||
|
if ip == nil {
|
||||||
|
iplen = 16
|
||||||
|
ip = parseIPv6(ipstr)
|
||||||
|
}
|
||||||
nn, i, ok := dtoi(maskstr, 0)
|
nn, i, ok := dtoi(maskstr, 0)
|
||||||
if ip == nil || !ok || i != len(maskstr) || nn < 0 || nn > 8*len(ip) {
|
if ip == nil || !ok || i != len(maskstr) || nn < 0 || nn > 8*iplen {
|
||||||
return nil, nil, &SyntaxError{"CIDR address", s}
|
return nil, nil, &ParseError{"CIDR address", s}
|
||||||
}
|
}
|
||||||
n := uint(nn)
|
n := uint(nn)
|
||||||
if len(ip) == 4 {
|
if iplen == 4 {
|
||||||
v4mask := ^uint32(0xffffffff >> n)
|
v4mask := ^uint32(0xffffffff >> n)
|
||||||
mask = IPMask(IPv4(byte(v4mask>>24), byte(v4mask>>16), byte(v4mask>>8), byte(v4mask)))
|
mask = IPv4Mask(byte(v4mask>>24), byte(v4mask>>16), byte(v4mask>>8), byte(v4mask))
|
||||||
return ip, mask, nil
|
} else {
|
||||||
}
|
mask = make(IPMask, 16)
|
||||||
mask = make(IPMask, 16)
|
for i := 0; i < 16; i++ {
|
||||||
for i := 0; i < 16; i++ {
|
if n >= 8 {
|
||||||
if n >= 8 {
|
mask[i] = 0xff
|
||||||
mask[i] = 0xff
|
n -= 8
|
||||||
n -= 8
|
continue
|
||||||
continue
|
}
|
||||||
|
mask[i] = ^byte(0xff >> n)
|
||||||
|
n = 0
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// address must not have any bits not in mask
|
||||||
|
for i := range ip {
|
||||||
|
if ip[i]&^mask[i] != 0 {
|
||||||
|
return nil, nil, &ParseError{"CIDR address", s}
|
||||||
}
|
}
|
||||||
mask[i] = ^byte(0xff >> n)
|
|
||||||
n = 0
|
|
||||||
}
|
}
|
||||||
return ip, mask, nil
|
return ip, mask, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,30 +5,26 @@
|
||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func isEqual(a, b IP) bool {
|
func isEqual(a, b []byte) bool {
|
||||||
if a == nil && b == nil {
|
if a == nil && b == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if a == nil || b == nil || len(a) != len(b) {
|
if a == nil || b == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for i := 0; i < len(a); i++ {
|
return bytes.Equal(a, b)
|
||||||
if a[i] != b[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type parseIPTest struct {
|
var parseiptests = []struct {
|
||||||
in string
|
in string
|
||||||
out IP
|
out IP
|
||||||
}
|
}{
|
||||||
|
|
||||||
var parseiptests = []parseIPTest{
|
|
||||||
{"127.0.1.2", IPv4(127, 0, 1, 2)},
|
{"127.0.1.2", IPv4(127, 0, 1, 2)},
|
||||||
{"127.0.0.1", IPv4(127, 0, 0, 1)},
|
{"127.0.0.1", IPv4(127, 0, 0, 1)},
|
||||||
{"127.0.0.256", nil},
|
{"127.0.0.256", nil},
|
||||||
|
@ -43,20 +39,17 @@ var parseiptests = []parseIPTest{
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseIP(t *testing.T) {
|
func TestParseIP(t *testing.T) {
|
||||||
for i := 0; i < len(parseiptests); i++ {
|
for _, tt := range parseiptests {
|
||||||
tt := parseiptests[i]
|
|
||||||
if out := ParseIP(tt.in); !isEqual(out, tt.out) {
|
if out := ParseIP(tt.in); !isEqual(out, tt.out) {
|
||||||
t.Errorf("ParseIP(%#q) = %v, want %v", tt.in, out, tt.out)
|
t.Errorf("ParseIP(%#q) = %v, want %v", tt.in, out, tt.out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ipStringTest struct {
|
var ipstringtests = []struct {
|
||||||
in IP
|
in IP
|
||||||
out string
|
out string
|
||||||
}
|
}{
|
||||||
|
|
||||||
var ipstringtests = []ipStringTest{
|
|
||||||
// cf. RFC 5952 (A Recommendation for IPv6 Address Text Representation)
|
// cf. RFC 5952 (A Recommendation for IPv6 Address Text Representation)
|
||||||
{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0,
|
{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0,
|
||||||
0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1},
|
0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1},
|
||||||
|
@ -85,10 +78,67 @@ var ipstringtests = []ipStringTest{
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIPString(t *testing.T) {
|
func TestIPString(t *testing.T) {
|
||||||
for i := 0; i < len(ipstringtests); i++ {
|
for _, tt := range ipstringtests {
|
||||||
tt := ipstringtests[i]
|
|
||||||
if out := tt.in.String(); out != tt.out {
|
if out := tt.in.String(); out != tt.out {
|
||||||
t.Errorf("IP.String(%v) = %#q, want %#q", tt.in, out, tt.out)
|
t.Errorf("IP.String(%v) = %#q, want %#q", tt.in, out, tt.out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parsecidrtests = []struct {
|
||||||
|
in string
|
||||||
|
ip IP
|
||||||
|
mask IPMask
|
||||||
|
err os.Error
|
||||||
|
}{
|
||||||
|
{"135.104.0.0/32", IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 255), nil},
|
||||||
|
{"0.0.0.0/24", IPv4(0, 0, 0, 0), IPv4Mask(255, 255, 255, 0), nil},
|
||||||
|
{"135.104.0.0/24", IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0), nil},
|
||||||
|
{"135.104.0.1/32", IPv4(135, 104, 0, 1), IPv4Mask(255, 255, 255, 255), nil},
|
||||||
|
{"135.104.0.1/24", nil, nil, &ParseError{"CIDR address", "135.104.0.1/24"}},
|
||||||
|
{"::1/128", ParseIP("::1"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")), nil},
|
||||||
|
{"abcd:2345::/127", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe")), nil},
|
||||||
|
{"abcd:2345::/65", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::")), nil},
|
||||||
|
{"abcd:2345::/64", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff::")), nil},
|
||||||
|
{"abcd:2345::/63", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:fffe::")), nil},
|
||||||
|
{"abcd:2345::/33", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:8000::")), nil},
|
||||||
|
{"abcd:2345::/32", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff::")), nil},
|
||||||
|
{"abcd:2344::/31", ParseIP("abcd:2344::"), IPMask(ParseIP("ffff:fffe::")), nil},
|
||||||
|
{"abcd:2300::/24", ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::")), nil},
|
||||||
|
{"abcd:2345::/24", nil, nil, &ParseError{"CIDR address", "abcd:2345::/24"}},
|
||||||
|
{"2001:DB8::/48", ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::")), nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseCIDR(t *testing.T) {
|
||||||
|
for _, tt := range parsecidrtests {
|
||||||
|
if ip, mask, err := ParseCIDR(tt.in); !isEqual(ip, tt.ip) || !isEqual(mask, tt.mask) || !reflect.DeepEqual(err, tt.err) {
|
||||||
|
t.Errorf("ParseCIDR(%q) = %v, %v, %v; want %v, %v, %v", tt.in, ip, mask, err, tt.ip, tt.mask, tt.err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var splitjointests = []struct {
|
||||||
|
Host string
|
||||||
|
Port string
|
||||||
|
Join string
|
||||||
|
}{
|
||||||
|
{"www.google.com", "80", "www.google.com:80"},
|
||||||
|
{"127.0.0.1", "1234", "127.0.0.1:1234"},
|
||||||
|
{"::1", "80", "[::1]:80"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSplitHostPort(t *testing.T) {
|
||||||
|
for _, tt := range splitjointests {
|
||||||
|
if host, port, err := SplitHostPort(tt.Join); host != tt.Host || port != tt.Port || err != nil {
|
||||||
|
t.Errorf("SplitHostPort(%q) = %q, %q, %v; want %q, %q, nil", tt.Join, host, port, err, tt.Host, tt.Port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJoinHostPort(t *testing.T) {
|
||||||
|
for _, tt := range splitjointests {
|
||||||
|
if join := JoinHostPort(tt.Host, tt.Port); join != tt.Join {
|
||||||
|
t.Errorf("JoinHostPort(%q, %q) = %q; want %q", tt.Host, tt.Port, join, tt.Join)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -240,7 +240,7 @@ func hostToIP(host string) (ip IP, err os.Error) {
|
||||||
addr = ParseIP(host)
|
addr = ParseIP(host)
|
||||||
if addr == nil {
|
if addr == nil {
|
||||||
// Not an IP address. Try as a DNS name.
|
// Not an IP address. Try as a DNS name.
|
||||||
_, addrs, err1 := LookupHost(host)
|
addrs, err1 := LookupHost(host)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
err = err1
|
err = err1
|
||||||
goto Error
|
goto Error
|
||||||
|
|
|
@ -170,9 +170,10 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
|
||||||
return nil, InvalidAddrError("unexpected socket family")
|
return nil, InvalidAddrError("unexpected socket family")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split "host:port" into "host" and "port".
|
// SplitHostPort splits a network address of the form
|
||||||
// Host cannot contain colons unless it is bracketed.
|
// "host:port" or "[host]:port" into host and port.
|
||||||
func splitHostPort(hostport string) (host, port string, err os.Error) {
|
// The latter form must be used when host contains a colon.
|
||||||
|
func SplitHostPort(hostport string) (host, port string, err os.Error) {
|
||||||
// The port starts after the last colon.
|
// The port starts after the last colon.
|
||||||
i := last(hostport, ':')
|
i := last(hostport, ':')
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
|
@ -195,9 +196,9 @@ func splitHostPort(hostport string) (host, port string, err os.Error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join "host" and "port" into "host:port".
|
// JoinHostPort combines host and port into a network address
|
||||||
// If host contains colons, will join into "[host]:port".
|
// of the form "host:port" or, if host contains a colon, "[host]:port".
|
||||||
func joinHostPort(host, port string) string {
|
func JoinHostPort(host, port string) string {
|
||||||
// If host has colons, have to bracket it.
|
// If host has colons, have to bracket it.
|
||||||
if byteIndex(host, ':') >= 0 {
|
if byteIndex(host, ':') >= 0 {
|
||||||
return "[" + host + "]:" + port
|
return "[" + host + "]:" + port
|
||||||
|
@ -207,7 +208,7 @@ func joinHostPort(host, port string) string {
|
||||||
|
|
||||||
// Convert "host:port" into IP address and port.
|
// Convert "host:port" into IP address and port.
|
||||||
func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
|
func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
|
||||||
host, port, err := splitHostPort(hostport)
|
host, port, err := SplitHostPort(hostport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
|
@ -218,7 +219,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
|
||||||
addr = ParseIP(host)
|
addr = ParseIP(host)
|
||||||
if addr == nil {
|
if addr == nil {
|
||||||
// Not an IP address. Try as a DNS name.
|
// Not an IP address. Try as a DNS name.
|
||||||
_, addrs, err1 := LookupHost(host)
|
addrs, err1 := LookupHost(host)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
err = err1
|
err = err1
|
||||||
goto Error
|
goto Error
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2011 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 net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LookupHost looks up the given host using the local resolver.
|
||||||
|
// It returns an array of that host's addresses.
|
||||||
|
func LookupHost(host string) (addrs []string, err os.Error) {
|
||||||
|
addrs, err, ok := cgoLookupHost(host)
|
||||||
|
if !ok {
|
||||||
|
addrs, err = goLookupHost(host)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupIP looks up host using the local resolver.
|
||||||
|
// It returns an array of that host's IPv4 and IPv6 addresses.
|
||||||
|
func LookupIP(host string) (addrs []IP, err os.Error) {
|
||||||
|
addrs, err, ok := cgoLookupIP(host)
|
||||||
|
if !ok {
|
||||||
|
addrs, err = goLookupIP(host)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupPort looks up the port for the given network and service.
|
||||||
|
func LookupPort(network, service string) (port int, err os.Error) {
|
||||||
|
port, err, ok := cgoLookupPort(network, service)
|
||||||
|
if !ok {
|
||||||
|
port, err = goLookupPort(network, service)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -15,50 +15,49 @@ var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check f
|
||||||
|
|
||||||
type DialErrorTest struct {
|
type DialErrorTest struct {
|
||||||
Net string
|
Net string
|
||||||
Laddr string
|
|
||||||
Raddr string
|
Raddr string
|
||||||
Pattern string
|
Pattern string
|
||||||
}
|
}
|
||||||
|
|
||||||
var dialErrorTests = []DialErrorTest{
|
var dialErrorTests = []DialErrorTest{
|
||||||
{
|
{
|
||||||
"datakit", "", "mh/astro/r70",
|
"datakit", "mh/astro/r70",
|
||||||
"dial datakit mh/astro/r70: unknown network datakit",
|
"dial datakit mh/astro/r70: unknown network datakit",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tcp", "", "127.0.0.1:☺",
|
"tcp", "127.0.0.1:☺",
|
||||||
"dial tcp 127.0.0.1:☺: unknown port tcp/☺",
|
"dial tcp 127.0.0.1:☺: unknown port tcp/☺",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tcp", "", "no-such-name.google.com.:80",
|
"tcp", "no-such-name.google.com.:80",
|
||||||
"dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.( on .*)?: no (.*)",
|
"dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.( on .*)?: no (.*)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tcp", "", "no-such-name.no-such-top-level-domain.:80",
|
"tcp", "no-such-name.no-such-top-level-domain.:80",
|
||||||
"dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.( on .*)?: no (.*)",
|
"dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.( on .*)?: no (.*)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tcp", "", "no-such-name:80",
|
"tcp", "no-such-name:80",
|
||||||
`dial tcp no-such-name:80: lookup no-such-name\.(.*\.)?( on .*)?: no (.*)`,
|
`dial tcp no-such-name:80: lookup no-such-name\.(.*\.)?( on .*)?: no (.*)`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tcp", "", "mh/astro/r70:http",
|
"tcp", "mh/astro/r70:http",
|
||||||
"dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name",
|
"dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"unix", "", "/etc/file-not-found",
|
"unix", "/etc/file-not-found",
|
||||||
"dial unix /etc/file-not-found: [nN]o such file or directory",
|
"dial unix /etc/file-not-found: [nN]o such file or directory",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"unix", "", "/etc/",
|
"unix", "/etc/",
|
||||||
"dial unix /etc/: ([pP]ermission denied|[sS]ocket operation on non-socket|[cC]onnection refused)",
|
"dial unix /etc/: ([pP]ermission denied|socket operation on non-socket|connection refused)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"unixpacket", "", "/etc/file-not-found",
|
"unixpacket", "/etc/file-not-found",
|
||||||
"dial unixpacket /etc/file-not-found: no such file or directory",
|
"dial unixpacket /etc/file-not-found: no such file or directory",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"unixpacket", "", "/etc/",
|
"unixpacket", "/etc/",
|
||||||
"dial unixpacket /etc/: (permission denied|socket operation on non-socket|connection refused)",
|
"dial unixpacket /etc/: (permission denied|socket operation on non-socket|connection refused)",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -69,7 +68,7 @@ func TestDialError(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for i, tt := range dialErrorTests {
|
for i, tt := range dialErrorTests {
|
||||||
c, e := Dial(tt.Net, tt.Laddr, tt.Raddr)
|
c, e := Dial(tt.Net, tt.Raddr)
|
||||||
if c != nil {
|
if c != nil {
|
||||||
c.Close()
|
c.Close()
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,8 +50,8 @@ func readServices() {
|
||||||
file.close()
|
file.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupPort looks up the port for the given network and service.
|
// goLookupPort is the native Go implementation of LookupPort.
|
||||||
func LookupPort(network, service string) (port int, err os.Error) {
|
func goLookupPort(network, service string) (port int, err os.Error) {
|
||||||
onceReadServices.Do(readServices)
|
onceReadServices.Do(readServices)
|
||||||
|
|
||||||
switch network {
|
switch network {
|
||||||
|
|
|
@ -54,13 +54,15 @@ func runServe(t *testing.T, network, addr string, listening chan<- string, done
|
||||||
}
|
}
|
||||||
|
|
||||||
func connect(t *testing.T, network, addr string, isEmpty bool) {
|
func connect(t *testing.T, network, addr string, isEmpty bool) {
|
||||||
var laddr string
|
var fd Conn
|
||||||
|
var err os.Error
|
||||||
if network == "unixgram" {
|
if network == "unixgram" {
|
||||||
laddr = addr + ".local"
|
fd, err = DialUnix(network, &UnixAddr{addr + ".local", network}, &UnixAddr{addr, network})
|
||||||
|
} else {
|
||||||
|
fd, err = Dial(network, addr)
|
||||||
}
|
}
|
||||||
fd, err := Dial(network, laddr, addr)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("net.Dial(%q, %q, %q) = _, %v", network, laddr, addr, err)
|
t.Fatalf("net.Dial(%q, %q) = _, %v", network, addr, err)
|
||||||
}
|
}
|
||||||
fd.SetReadTimeout(1e9) // 1s
|
fd.SetReadTimeout(1e9) // 1s
|
||||||
|
|
||||||
|
|
|
@ -52,14 +52,16 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fd, err = newFD(s, f, p, net); err != nil {
|
||||||
|
closesocket(s)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if ra != nil {
|
if ra != nil {
|
||||||
e = syscall.Connect(s, ra)
|
if err = fd.connect(ra); err != nil {
|
||||||
for e == syscall.EINTR {
|
fd.sysfd = -1
|
||||||
e = syscall.Connect(s, ra)
|
|
||||||
}
|
|
||||||
if e != 0 {
|
|
||||||
closesocket(s)
|
closesocket(s)
|
||||||
return nil, os.Errno(e)
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,12 +70,7 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal
|
||||||
sa, _ = syscall.Getpeername(s)
|
sa, _ = syscall.Getpeername(s)
|
||||||
raddr := toAddr(sa)
|
raddr := toAddr(sa)
|
||||||
|
|
||||||
fd, err = newFD(s, f, p, net, laddr, raddr)
|
fd.setAddr(laddr, raddr)
|
||||||
if err != nil {
|
|
||||||
closesocket(s)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return fd, nil
|
return fd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,9 +167,9 @@ func (e *UnknownSocketError) String() string {
|
||||||
func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) {
|
func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) {
|
||||||
switch a := sa.(type) {
|
switch a := sa.(type) {
|
||||||
case *syscall.SockaddrInet4:
|
case *syscall.SockaddrInet4:
|
||||||
return joinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
|
return JoinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
|
||||||
case *syscall.SockaddrInet6:
|
case *syscall.SockaddrInet6:
|
||||||
return joinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
|
return JoinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
|
||||||
case *syscall.SockaddrUnix:
|
case *syscall.SockaddrUnix:
|
||||||
return a.Name, nil
|
return a.Name, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ func (a *TCPAddr) String() string {
|
||||||
if a == nil {
|
if a == nil {
|
||||||
return "<nil>"
|
return "<nil>"
|
||||||
}
|
}
|
||||||
return joinHostPort(a.IP.String(), itoa(a.Port))
|
return JoinHostPort(a.IP.String(), itoa(a.Port))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *TCPAddr) family() int {
|
func (a *TCPAddr) family() int {
|
||||||
|
@ -213,8 +213,9 @@ func (c *TCPConn) SetNoDelay(noDelay bool) os.Error {
|
||||||
// Closing c does not affect f, and closing f does not affect c.
|
// Closing c does not affect f, and closing f does not affect c.
|
||||||
func (c *TCPConn) File() (f *os.File, err os.Error) { return c.fd.dup() }
|
func (c *TCPConn) File() (f *os.File, err os.Error) { return c.fd.dup() }
|
||||||
|
|
||||||
// DialTCP is like Dial but can only connect to TCP networks
|
// DialTCP connects to the remote address raddr on the network net,
|
||||||
// and returns a TCPConn structure.
|
// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
|
||||||
|
// as the local address for the connection.
|
||||||
func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error) {
|
func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error) {
|
||||||
if raddr == nil {
|
if raddr == nil {
|
||||||
return nil, &OpError{"dial", "tcp", nil, errMissingAddress}
|
return nil, &OpError{"dial", "tcp", nil, errMissingAddress}
|
||||||
|
|
|
@ -78,7 +78,7 @@ func (c *Conn) Close() os.Error {
|
||||||
// Dial connects to the given address on the given network using net.Dial
|
// Dial connects to the given address on the given network using net.Dial
|
||||||
// and then returns a new Conn for the connection.
|
// and then returns a new Conn for the connection.
|
||||||
func Dial(network, addr string) (*Conn, os.Error) {
|
func Dial(network, addr string) (*Conn, os.Error) {
|
||||||
c, err := net.Dial(network, "", addr)
|
c, err := net.Dial(network, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func testTimeout(t *testing.T, network, addr string, readFrom bool) {
|
func testTimeout(t *testing.T, network, addr string, readFrom bool) {
|
||||||
fd, err := Dial(network, "", addr)
|
fd, err := Dial(network, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("dial %s %s failed: %v", network, addr, err)
|
t.Errorf("dial %s %s failed: %v", network, addr, err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -34,7 +34,7 @@ func (a *UDPAddr) String() string {
|
||||||
if a == nil {
|
if a == nil {
|
||||||
return "<nil>"
|
return "<nil>"
|
||||||
}
|
}
|
||||||
return joinHostPort(a.IP.String(), itoa(a.Port))
|
return JoinHostPort(a.IP.String(), itoa(a.Port))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *UDPAddr) family() int {
|
func (a *UDPAddr) family() int {
|
||||||
|
|
|
@ -48,7 +48,7 @@ func NewImporter(conn io.ReadWriter) *Importer {
|
||||||
|
|
||||||
// Import imports a set of channels from the given network and address.
|
// Import imports a set of channels from the given network and address.
|
||||||
func Import(network, remoteaddr string) (*Importer, os.Error) {
|
func Import(network, remoteaddr string) (*Importer, os.Error) {
|
||||||
conn, err := net.Dial(network, "", remoteaddr)
|
conn, err := net.Dial(network, remoteaddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -399,7 +399,7 @@ func TestImportFlowControl(t *testing.T) {
|
||||||
|
|
||||||
func testFlow(sendDone chan bool, ch <-chan int, N int, t *testing.T) {
|
func testFlow(sendDone chan bool, ch <-chan int, N int, t *testing.T) {
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(1e9)
|
time.Sleep(0.5e9)
|
||||||
sendDone <- false
|
sendDone <- false
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -108,3 +108,21 @@ func Truncate(name string, size int64) Error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// basename removes trailing slashes and the leading directory name from path name
|
||||||
|
func basename(name string) string {
|
||||||
|
i := len(name) - 1
|
||||||
|
// Remove trailing slashes
|
||||||
|
for ; i > 0 && name[i] == '/'; i-- {
|
||||||
|
name = name[:i]
|
||||||
|
}
|
||||||
|
// Remove leading directory name
|
||||||
|
for i--; i >= 0; i-- {
|
||||||
|
if name[i] == '/' {
|
||||||
|
name = name[i+1:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
|
@ -208,7 +208,7 @@ func DialHTTP(network, address string) (*Client, os.Error) {
|
||||||
// at the specified network address and path.
|
// at the specified network address and path.
|
||||||
func DialHTTPPath(network, address, path string) (*Client, os.Error) {
|
func DialHTTPPath(network, address, path string) (*Client, os.Error) {
|
||||||
var err os.Error
|
var err os.Error
|
||||||
conn, err := net.Dial(network, "", address)
|
conn, err := net.Dial(network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -229,7 +229,7 @@ func DialHTTPPath(network, address, path string) (*Client, os.Error) {
|
||||||
|
|
||||||
// Dial connects to an RPC server at the specified network address.
|
// Dial connects to an RPC server at the specified network address.
|
||||||
func Dial(network, address string) (*Client, os.Error) {
|
func Dial(network, address string) (*Client, os.Error) {
|
||||||
conn, err := net.Dial(network, "", address)
|
conn, err := net.Dial(network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@ func NewClient(conn io.ReadWriteCloser) *rpc.Client {
|
||||||
|
|
||||||
// Dial connects to a JSON-RPC server at the specified network address.
|
// Dial connects to a JSON-RPC server at the specified network address.
|
||||||
func Dial(network, address string) (*rpc.Client, os.Error) {
|
func Dial(network, address string) (*rpc.Client, os.Error) {
|
||||||
conn, err := net.Dial(network, "", address)
|
conn, err := net.Dial(network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCPUProfile(t *testing.T) {
|
func TestCPUProfile(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
// see Apple Bug Report #9177434 (copied into change description)
|
||||||
|
return
|
||||||
|
case "plan9":
|
||||||
|
// unimplemented
|
||||||
|
return
|
||||||
|
case "windows":
|
||||||
|
// unimplemented
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ type Client struct {
|
||||||
|
|
||||||
// Dial returns a new Client connected to an SMTP server at addr.
|
// Dial returns a new Client connected to an SMTP server at addr.
|
||||||
func Dial(addr string) (*Client, os.Error) {
|
func Dial(addr string) (*Client, os.Error) {
|
||||||
conn, err := net.Dial("tcp", "", addr)
|
conn, err := net.Dial("tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,11 @@ func TestSortStrings(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSortLarge_Random(t *testing.T) {
|
func TestSortLarge_Random(t *testing.T) {
|
||||||
data := make([]int, 1000000)
|
n := 1000000
|
||||||
|
if testing.Short() {
|
||||||
|
n /= 100
|
||||||
|
}
|
||||||
|
data := make([]int, n)
|
||||||
for i := 0; i < len(data); i++ {
|
for i := 0; i < len(data); i++ {
|
||||||
data[i] = rand.Intn(100)
|
data[i] = rand.Intn(100)
|
||||||
}
|
}
|
||||||
|
@ -174,6 +178,9 @@ func lg(n int) int {
|
||||||
|
|
||||||
func TestBentleyMcIlroy(t *testing.T) {
|
func TestBentleyMcIlroy(t *testing.T) {
|
||||||
sizes := []int{100, 1023, 1024, 1025}
|
sizes := []int{100, 1023, 1024, 1025}
|
||||||
|
if testing.Short() {
|
||||||
|
sizes = []int{100, 127, 128, 129}
|
||||||
|
}
|
||||||
dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"}
|
dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"}
|
||||||
modes := []string{"copy", "reverse", "reverse1", "reverse2", "sort", "dither"}
|
modes := []string{"copy", "reverse", "reverse1", "reverse2", "sort", "dither"}
|
||||||
var tmp1, tmp2 [1025]int
|
var tmp1, tmp2 [1025]int
|
||||||
|
|
|
@ -275,20 +275,10 @@ func Join(a []string, sep string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
b := make([]byte, n)
|
b := make([]byte, n)
|
||||||
bp := 0
|
bp := copy(b, a[0])
|
||||||
for i := 0; i < len(a); i++ {
|
for _, s := range a[1:] {
|
||||||
s := a[i]
|
bp += copy(b[bp:], sep)
|
||||||
for j := 0; j < len(s); j++ {
|
bp += copy(b[bp:], s)
|
||||||
b[bp] = s[j]
|
|
||||||
bp++
|
|
||||||
}
|
|
||||||
if i+1 < len(a) {
|
|
||||||
s = sep
|
|
||||||
for j := 0; j < len(s); j++ {
|
|
||||||
b[bp] = s[j]
|
|
||||||
bp++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
@ -312,9 +302,19 @@ func Map(mapping func(rune int) int, s string) string {
|
||||||
// fine. It could also shrink but that falls out naturally.
|
// fine. It could also shrink but that falls out naturally.
|
||||||
maxbytes := len(s) // length of b
|
maxbytes := len(s) // length of b
|
||||||
nbytes := 0 // number of bytes encoded in b
|
nbytes := 0 // number of bytes encoded in b
|
||||||
b := make([]byte, maxbytes)
|
// The output buffer b is initialized on demand, the first
|
||||||
for _, c := range s {
|
// time a character differs.
|
||||||
|
var b []byte
|
||||||
|
|
||||||
|
for i, c := range s {
|
||||||
rune := mapping(c)
|
rune := mapping(c)
|
||||||
|
if b == nil {
|
||||||
|
if rune == c {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b = make([]byte, maxbytes)
|
||||||
|
nbytes = copy(b, s[:i])
|
||||||
|
}
|
||||||
if rune >= 0 {
|
if rune >= 0 {
|
||||||
wid := 1
|
wid := 1
|
||||||
if rune >= utf8.RuneSelf {
|
if rune >= utf8.RuneSelf {
|
||||||
|
@ -330,6 +330,9 @@ func Map(mapping func(rune int) int, s string) string {
|
||||||
nbytes += utf8.EncodeRune(b[nbytes:maxbytes], rune)
|
nbytes += utf8.EncodeRune(b[nbytes:maxbytes], rune)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if b == nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
return string(b[0:nbytes])
|
return string(b[0:nbytes])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,12 @@ package strings_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
. "strings"
|
. "strings"
|
||||||
"testing"
|
"testing"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
"unsafe"
|
||||||
"utf8"
|
"utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -429,12 +431,32 @@ func TestMap(t *testing.T) {
|
||||||
if m != expect {
|
if m != expect {
|
||||||
t.Errorf("drop: expected %q got %q", expect, m)
|
t.Errorf("drop: expected %q got %q", expect, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 6. Identity
|
||||||
|
identity := func(rune int) int {
|
||||||
|
return rune
|
||||||
|
}
|
||||||
|
orig := "Input string that we expect not to be copied."
|
||||||
|
m = Map(identity, orig)
|
||||||
|
if (*reflect.StringHeader)(unsafe.Pointer(&orig)).Data !=
|
||||||
|
(*reflect.StringHeader)(unsafe.Pointer(&m)).Data {
|
||||||
|
t.Error("unexpected copy during identity map")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestToUpper(t *testing.T) { runStringTests(t, ToUpper, "ToUpper", upperTests) }
|
func TestToUpper(t *testing.T) { runStringTests(t, ToUpper, "ToUpper", upperTests) }
|
||||||
|
|
||||||
func TestToLower(t *testing.T) { runStringTests(t, ToLower, "ToLower", lowerTests) }
|
func TestToLower(t *testing.T) { runStringTests(t, ToLower, "ToLower", lowerTests) }
|
||||||
|
|
||||||
|
func BenchmarkMapNoChanges(b *testing.B) {
|
||||||
|
identity := func(rune int) int {
|
||||||
|
return rune
|
||||||
|
}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Map(identity, "Some string that won't be modified.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSpecialCase(t *testing.T) {
|
func TestSpecialCase(t *testing.T) {
|
||||||
lower := "abcçdefgğhıijklmnoöprsştuüvyz"
|
lower := "abcçdefgğhıijklmnoöprsştuüvyz"
|
||||||
upper := "ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZ"
|
upper := "ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZ"
|
||||||
|
@ -617,7 +639,11 @@ func equal(m string, s1, s2 string, t *testing.T) bool {
|
||||||
|
|
||||||
func TestCaseConsistency(t *testing.T) {
|
func TestCaseConsistency(t *testing.T) {
|
||||||
// Make a string of all the runes.
|
// Make a string of all the runes.
|
||||||
a := make([]int, unicode.MaxRune+1)
|
numRunes := unicode.MaxRune + 1
|
||||||
|
if testing.Short() {
|
||||||
|
numRunes = 1000
|
||||||
|
}
|
||||||
|
a := make([]int, numRunes)
|
||||||
for i := range a {
|
for i := range a {
|
||||||
a[i] = i
|
a[i] = i
|
||||||
}
|
}
|
||||||
|
@ -627,10 +653,10 @@ func TestCaseConsistency(t *testing.T) {
|
||||||
lower := ToLower(s)
|
lower := ToLower(s)
|
||||||
|
|
||||||
// Consistency checks
|
// Consistency checks
|
||||||
if n := utf8.RuneCountInString(upper); n != unicode.MaxRune+1 {
|
if n := utf8.RuneCountInString(upper); n != numRunes {
|
||||||
t.Error("rune count wrong in upper:", n)
|
t.Error("rune count wrong in upper:", n)
|
||||||
}
|
}
|
||||||
if n := utf8.RuneCountInString(lower); n != unicode.MaxRune+1 {
|
if n := utf8.RuneCountInString(lower); n != numRunes {
|
||||||
t.Error("rune count wrong in lower:", n)
|
t.Error("rune count wrong in lower:", n)
|
||||||
}
|
}
|
||||||
if !equal("ToUpper(upper)", ToUpper(upper), upper, t) {
|
if !equal("ToUpper(upper)", ToUpper(upper), upper, t) {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue