Update to current Go library.

From-SVN: r171732
This commit is contained in:
Ian Lance Taylor 2011-03-30 15:33:16 +00:00
parent f2034d064c
commit f72f416913
114 changed files with 1924 additions and 676 deletions

View File

@ -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, ""
}

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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
}
} }
} }

View File

@ -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
} }

View File

@ -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}},
} }

View File

@ -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

View File

@ -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
}
} }
} }

View File

@ -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))
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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()

View File

@ -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),

View File

@ -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
}
} }
} }

View File

@ -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
}
} }
} }

View File

@ -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
}
} }
} }

View File

@ -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)

View File

@ -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`

View File

@ -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
}
} }
} }

View File

@ -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

View File

@ -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
} }

View File

@ -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
}

View File

@ -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)
} }

View File

@ -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)

View File

@ -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
} }

View File

@ -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

View File

@ -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

View File

@ -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" .`,
`,
} }

View File

@ -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.

View File

@ -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 = "@"

View File

@ -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())

View File

@ -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)
} }
} }

View File

@ -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()
} }

View File

@ -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
} }

View File

@ -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("")

View File

@ -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
} }

View File

@ -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}
} }

188
libgo/go/go/ast/resolve.go Normal file
View File

@ -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)
}

View File

@ -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 {

View File

@ -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

View File

@ -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]:]

View File

@ -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

View File

@ -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}
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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
}
} }
} }

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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])
} }

View File

@ -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))
} }
} }

View File

@ -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
} }

View File

@ -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())
} }
} }

View File

@ -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
} }
} }
} }

View File

@ -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])})
} }

View File

@ -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")
} }
} }

View File

@ -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())
} }
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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)
} }

View File

@ -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
} }

View File

@ -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 {

View File

@ -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)

View File

@ -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)

View File

@ -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{} {

21
libgo/go/net/cgo_stub.go Normal file
View File

@ -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
}

View File

@ -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
} }

View File

@ -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, "%") {

View File

@ -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)

View File

@ -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())
} }

View File

@ -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
} }

View File

@ -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.

119
libgo/go/net/file.go Normal file
View File

@ -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
}

131
libgo/go/net/file_test.go Normal file
View File

@ -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")
}
}

View File

@ -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)
}

View File

@ -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),

View File

@ -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
} }

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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

38
libgo/go/net/lookup.go Normal file
View File

@ -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
}

View File

@ -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()
} }

View File

@ -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 {

View File

@ -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

View File

@ -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
} }

View File

@ -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}

View File

@ -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
} }

View File

@ -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

View File

@ -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 {

View File

@ -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
} }

View File

@ -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
}() }()

View File

@ -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
}

View File

@ -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
} }

View File

@ -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
} }

View File

@ -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
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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])
} }

View File

@ -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