From: Ian Lance Taylor
+// Turn each run of multiple \n into
. // Turn each run of indented lines into a
block without indent. // // URLs in the comment text are converted into links; if the URL also appears diff --git a/libgo/go/go/doc/doc.go b/libgo/go/go/doc/doc.go index e46857cb8a1..e7a8d3f63bb 100644 --- a/libgo/go/go/doc/doc.go +++ b/libgo/go/go/doc/doc.go @@ -66,7 +66,7 @@ func (doc *docReader) addDoc(comments *ast.CommentGroup) { n2 := len(comments.List) list := make([]*ast.Comment, n1+1+n2) // + 1 for separator line 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) doc.doc = &ast.CommentGroup{list} } @@ -105,7 +105,7 @@ func baseTypeName(typ ast.Expr) string { // if the type is not exported, the effect to // a client is as if there were no type name if t.IsExported() { - return string(t.Name) + return t.Name } case *ast.StarExpr: return baseTypeName(t.X) @@ -300,9 +300,9 @@ func (doc *docReader) addFile(src *ast.File) { // collect BUG(...) comments for _, c := range src.Comments { 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 - 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 list := copyCommentList(c.List) list[0].Text = text[m[1]:] diff --git a/libgo/go/go/parser/interface.go b/libgo/go/go/parser/interface.go index 6f35b495efa..fc4ae094394 100644 --- a/libgo/go/go/parser/interface.go +++ b/libgo/go/go/parser/interface.go @@ -69,7 +69,7 @@ func ParseExpr(fset *token.FileSet, filename string, src interface{}) (ast.Expr, var p parser p.init(fset, filename, data, 0) - x := p.parseExpr() + x := p.parseRhs() if p.tok == token.SEMICOLON { 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 pkg, found := pkgs[name] 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 } pkg.Files[filename] = src diff --git a/libgo/go/go/parser/parser.go b/libgo/go/go/parser/parser.go index b0e8c8ad7a8..ad7e4cdcf2e 100644 --- a/libgo/go/go/parser/parser.go +++ b/libgo/go/go/parser/parser.go @@ -47,17 +47,18 @@ type parser struct { lineComment *ast.CommentGroup // last line comment // Next token - pos token.Pos // token position - tok token.Token // one token look-ahead - lit_ []byte // token literal (slice into original source, don't hold on to it) + pos token.Pos // token position + tok token.Token // one token look-ahead + lit string // token literal // Non-syntactic parser control exprLev int // < 0: in control clause, >= 0: in expression // Ordinary identifer scopes - pkgScope *ast.Scope // pkgScope.Outer == nil - topScope *ast.Scope // top-most scope; may be pkgScope - unresolved []*ast.Ident // unresolved global identifiers + pkgScope *ast.Scope // pkgScope.Outer == nil + topScope *ast.Scope // top-most scope; may be pkgScope + unresolved []*ast.Ident // unresolved identifiers + imports []*ast.ImportSpec // list of imports // Label scope // (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 @@ -141,13 +133,13 @@ func (p *parser) closeLabelScope() { func (p *parser) declare(decl interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) { for _, ident := range idents { + assert(ident.Obj == nil, "identifier already declared or resolved") if ident.Name != "_" { obj := ast.NewObj(kind, ident.Name) // remember the corresponding declaration for redeclaration // errors and global variable resolution/typechecking phase obj.Decl = decl - alt := scope.Insert(obj) - if alt != obj && p.mode&DeclarationErrors != 0 { + if alt := scope.Insert(obj); alt != nil && p.mode&DeclarationErrors != 0 { prevDecl := "" if pos := alt.Pos(); pos.IsValid() { 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. n := 0 // number of new variables for _, ident := range idents { + assert(ident.Obj == nil, "identifier already declared or resolved") if ident.Name != "_" { obj := ast.NewObj(ast.Var, ident.Name) // short var declarations cannot have redeclaration errors // and are not global => no need to remember the respective // declaration alt := p.topScope.Insert(obj) - if alt == obj { + if alt == nil { n++ // new declaration + alt = obj } 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 == "_" { return } @@ -195,10 +201,12 @@ func (p *parser) resolve(ident *ast.Ident) { return } } - // collect unresolved global identifiers; ignore the others - if p.topScope == p.pkgScope { - p.unresolved = append(p.unresolved, ident) - } + // all local scopes are known, so any unresolved identifier + // must be found either in the file scope, package scope + // (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() switch { case p.tok.IsLiteral(): - p.printTrace(s, string(p.lit_)) + p.printTrace(s, p.lit) case p.tok.IsOperator(), p.tok.IsKeyword(): p.printTrace("\"" + s + "\"") 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. @@ -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. // Scan the comment for '\n' chars and adjust endline accordingly. endline = p.file.Line(p.pos) - if p.lit_[1] == '*' { - for _, b := range p.lit_ { - if b == '\n' { + if p.lit[1] == '*' { + // don't use range here - no need to decode Unicode code points + for i := 0; i < len(p.lit); i++ { + if p.lit[i] == '\n' { endline++ } } } - comment = &ast.Comment{p.pos, p.lit()} + comment = &ast.Comment{p.pos, p.lit} p.next0() return @@ -358,12 +367,12 @@ func (p *parser) errorExpected(pos token.Pos, msg string) { if pos == p.pos { // the error happened at the current position; // 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" } else { msg += ", found '" + p.tok.String() + "'" 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 @@ -395,7 +411,7 @@ func (p *parser) parseIdent() *ast.Ident { pos := p.pos name := "_" if p.tok == token.IDENT { - name = string(p.lit_) + name = p.lit p.next() } else { p.expect(token.IDENT) // use expect() error handling @@ -422,21 +438,51 @@ func (p *parser) parseIdentList() (list []*ast.Ident) { // ---------------------------------------------------------------------------- // 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 { defer un(trace(p, "ExpressionList")) } - list = append(list, p.parseExpr()) + list = append(list, p.parseExpr(lhs)) for p.tok == token.COMMA { p.next() - list = append(list, p.parseExpr()) + list = append(list, p.parseExpr(lhs)) } 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 @@ -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. +func (p *parser) parseTypeName() ast.Expr { if p.trace { - defer un(trace(p, "QualifiedIdent")) + defer un(trace(p, "TypeName")) } ident := p.parseIdent() - p.resolve(ident) - var x ast.Expr = ident + // don't resolve ident yet - it may be a parameter or field name + if p.tok == token.PERIOD { - // first identifier is a package identifier + // ident is a package name p.next() + p.resolve(ident) sel := p.parseIdent() - x = &ast.SelectorExpr{x, sel} + return &ast.SelectorExpr{ident, sel} } - return x -} - - -func (p *parser) parseTypeName() ast.Expr { - if p.trace { - defer un(trace(p, "TypeName")) - } - - return p.parseQualifiedIdent() + return ident } @@ -497,7 +536,7 @@ func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr { len = &ast.Ellipsis{p.pos, nil} p.next() } else if p.tok != token.RBRACK { - len = p.parseExpr() + len = p.parseRhs() } p.expect(token.RBRACK) 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 { defer un(trace(p, "FieldDecl")) } @@ -534,7 +573,7 @@ func (p *parser) parseFieldDecl() *ast.Field { // optional tag var tag *ast.BasicLit 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() } @@ -546,6 +585,7 @@ func (p *parser) parseFieldDecl() *ast.Field { } else { // ["*"] TypeName (AnonymousField) typ = list[0] // we always have at least one element + p.resolve(typ) if n := len(list); n > 1 || !isTypeName(deref(typ)) { pos := typ.Pos() p.errorExpected(pos, "anonymous field") @@ -555,7 +595,10 @@ func (p *parser) parseFieldDecl() *ast.Field { 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) lbrace := p.expect(token.LBRACE) + scope := ast.NewScope(nil) // struct scope var list []*ast.Field for p.tok == token.IDENT || p.tok == token.MUL || p.tok == token.LPAREN { // a field declaration cannot start with a '(' but we accept // it here for more robust parsing and better error messages // (parseFieldDecl will check and complain if necessary) - list = append(list, p.parseFieldDecl()) + list = append(list, p.parseFieldDecl(scope)) } rbrace := p.expect(token.RBRACE) + // TODO(gri): store struct scope in AST 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 { pos := p.pos 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 { p.error(pos, "'...' parameter is missing type") typ = &ast.BadExpr{pos, p.pos} @@ -605,7 +650,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr { } 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 typ = p.tryVarType(isParam) + if typ != nil { + p.resolve(typ) + } return } @@ -682,6 +730,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [ // Type { "," Type } (anonymous parameters) params = make([]*ast.Field, len(list)) for i, x := range list { + p.resolve(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 { defer un(trace(p, "MethodSpec")) } @@ -759,7 +808,7 @@ func (p *parser) parseMethodSpec() *ast.Field { doc := p.leadComment var idents []*ast.Ident var typ ast.Expr - x := p.parseQualifiedIdent() + x := p.parseTypeName() if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN { // method idents = []*ast.Ident{ident} @@ -772,7 +821,10 @@ func (p *parser) parseMethodSpec() *ast.Field { } 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) lbrace := p.expect(token.LBRACE) + scope := ast.NewScope(nil) // interface scope var list []*ast.Field for p.tok == token.IDENT { - list = append(list, p.parseMethodSpec()) + list = append(list, p.parseMethodSpec(scope)) } rbrace := p.expect(token.RBRACE) + // TODO(gri): store interface scope in AST 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 { case token.IDENT: 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 // 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 { defer un(trace(p, "Operand")) } switch p.tok { case token.IDENT: - ident := p.parseIdent() - p.resolve(ident) - return ident + x := p.parseIdent() + if !lhs { + p.resolve(x) + } + return x 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() return x @@ -960,7 +1024,7 @@ func (p *parser) parseOperand() ast.Expr { lparen := p.pos p.next() p.exprLev++ - x := p.parseExpr() + x := p.parseRhs() p.exprLev-- rparen := p.expect(token.RPAREN) return &ast.ParenExpr{lparen, x, rparen} @@ -969,9 +1033,11 @@ func (p *parser) parseOperand() ast.Expr { return p.parseFuncTypeOrLit() default: - t := p.tryRawType(true) // could be type for composite literal or conversion - if t != nil { - return t + if typ := p.tryIdentOrType(true); typ != nil { + // could be type for composite literal or conversion + _, 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 { - defer un(trace(p, "SelectorOrTypeAssertion")) + defer un(trace(p, "Selector")) } - p.expect(token.PERIOD) - if p.tok == token.IDENT { - // selector - sel := p.parseIdent() - 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) var typ ast.Expr if p.tok == token.TYPE { @@ -1019,13 +1088,13 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr { var low, high ast.Expr isSlice := false if p.tok != token.COLON { - low = p.parseExpr() + low = p.parseRhs() } if p.tok == token.COLON { isSlice = true p.next() if p.tok != token.RBRACK { - high = p.parseExpr() + high = p.parseRhs() } } p.exprLev-- @@ -1048,7 +1117,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr { var list []ast.Expr var ellipsis token.Pos 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 { ellipsis = p.pos p.next() @@ -1074,12 +1143,16 @@ func (p *parser) parseElement(keyOk bool) ast.Expr { return p.parseLiteralValue(nil) } - x := p.parseExpr() - if keyOk && p.tok == token.COLON { - colon := p.pos - p.next() - x = &ast.KeyValueExpr{x, colon, p.parseElement(false)} + x := p.parseExpr(keyOk) // don't resolve if map key + if keyOk { + if p.tok == token.COLON { + colon := p.pos + p.next() + return &ast.KeyValueExpr{x, colon, p.parseElement(false)} + } + p.resolve(x) // not a map key } + 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 { defer un(trace(p, "PrimaryExpr")) } - x := p.parseOperand() + x := p.parseOperand(lhs) L: for { switch p.tok { 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: + if lhs { + p.resolve(x) + } x = p.parseIndexOrSlice(p.checkExpr(x)) case token.LPAREN: + if lhs { + p.resolve(x) + } x = p.parseCallOrConversion(p.checkExprOrType(x)) case token.LBRACE: if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) { + if lhs { + p.resolve(x) + } x = p.parseLiteralValue(x) } else { break L @@ -1255,13 +1352,15 @@ L: default: break L } + lhs = false // no need to try to resolve again } 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 { 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: pos, op := p.pos, p.tok p.next() - x := p.parseUnaryExpr() + x := p.parseUnaryExpr(false) return &ast.UnaryExpr{pos, op, p.checkExpr(x)} case token.ARROW: @@ -1283,32 +1382,37 @@ func (p *parser) parseUnaryExpr() ast.Expr { return &ast.ChanType{pos, ast.RECV, value} } - x := p.parseUnaryExpr() + x := p.parseUnaryExpr(false) return &ast.UnaryExpr{pos, token.ARROW, p.checkExpr(x)} case token.MUL: // pointer type or unary "*" expression pos := p.pos p.next() - x := p.parseUnaryExpr() + x := p.parseUnaryExpr(false) 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 { defer un(trace(p, "BinaryExpr")) } - x := p.parseUnaryExpr() + x := p.parseUnaryExpr(lhs) for prec := p.tok.Precedence(); prec >= prec1; prec-- { for p.tok.Precedence() == prec { pos, op := p.pos, p.tok 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)} } } @@ -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) - // 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 { 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")) } - x := p.parseExprList() + x := p.parseLhsList() switch p.tok { case @@ -1347,10 +1457,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt { // assignment statement pos, tok := p.pos, p.tok p.next() - y := p.parseExprList() - if tok == token.DEFINE { - p.shortVarDecl(p.makeIdentList(x)) - } + y := p.parseRhsList() return &ast.AssignStmt{x, pos, tok, y} } @@ -1379,7 +1486,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt { // send statement arrow := p.pos p.next() // consume "<-" - y := p.parseExpr() + y := p.parseRhs() return &ast.SendStmt{x[0], arrow, y} case token.INC, token.DEC: @@ -1395,7 +1502,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt { func (p *parser) parseCallExpr() *ast.CallExpr { - x := p.parseExpr() + x := p.parseRhs() if call, isCall := x.(*ast.CallExpr); isCall { return call } @@ -1445,7 +1552,7 @@ func (p *parser) parseReturnStmt() *ast.ReturnStmt { p.expect(token.RETURN) var x []ast.Expr if p.tok != token.SEMICOLON && p.tok != token.RBRACE { - x = p.parseExprList() + x = p.parseRhsList() } p.expectSemi() @@ -1500,12 +1607,12 @@ func (p *parser) parseIfStmt() *ast.IfStmt { p.exprLev = -1 if p.tok == token.SEMICOLON { p.next() - x = p.parseExpr() + x = p.parseRhs() } else { s = p.parseSimpleStmt(false) if p.tok == token.SEMICOLON { p.next() - x = p.parseExpr() + x = p.parseRhs() } else { x = p.makeExpr(s) s = nil @@ -1552,7 +1659,7 @@ func (p *parser) parseCaseClause(exprSwitch bool) *ast.CaseClause { if p.tok == token.CASE { p.next() if exprSwitch { - list = p.parseExprList() + list = p.parseRhsList() } else { list = p.parseTypeList() } @@ -1639,7 +1746,7 @@ func (p *parser) parseCommClause() *ast.CommClause { var comm ast.Stmt if p.tok == token.CASE { p.next() - lhs := p.parseExprList() + lhs := p.parseLhsList() if p.tok == token.ARROW { // SendStmt if len(lhs) > 1 { @@ -1648,7 +1755,7 @@ func (p *parser) parseCommClause() *ast.CommClause { } arrow := p.pos p.next() - rhs := p.parseExpr() + rhs := p.parseRhs() comm = &ast.SendStmt{lhs[0], arrow, rhs} } else { // RecvStmt @@ -1663,10 +1770,7 @@ func (p *parser) parseCommClause() *ast.CommClause { lhs = lhs[0:2] } p.next() - rhs = p.parseExpr() - if tok == token.DEFINE { - p.shortVarDecl(p.makeIdentList(lhs)) - } + rhs = p.parseRhs() } else { // rhs must be single receive operation if len(lhs) > 1 { @@ -1866,14 +1970,18 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec { var path *ast.BasicLit 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() } else { p.expect(token.STRING) // use expect() error handling } 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 if typ != nil || p.tok == token.ASSIGN || iota == 0 { p.expect(token.ASSIGN) - values = p.parseExprList() + values = p.parseRhsList() } 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 if typ == nil || p.tok == token.ASSIGN { p.expect(token.ASSIGN) - values = p.parseExprList() + values = p.parseRhsList() } p.expectSemi() // call before accessing p.linecomment @@ -2120,20 +2228,20 @@ func (p *parser) parseFile() *ast.File { } } - if p.topScope != p.pkgScope { - panic("internal error: imbalanced scopes") - } + assert(p.topScope == p.pkgScope, "imbalanced scopes") // resolve global identifiers within the same file i := 0 for _, ident := range p.unresolved { // 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 { p.unresolved[i] = ident 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} } diff --git a/libgo/go/go/printer/nodes.go b/libgo/go/go/printer/nodes.go index 2238b6bedc8..0b3b6621e6c 100644 --- a/libgo/go/go/printer/nodes.go +++ b/libgo/go/go/printer/nodes.go @@ -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 // depend on any previous formatting prevBreak := -1 // index of last expression that was followed by a linebreak - linebreakMin := 1 - 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) { + if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) { ws = ignore *multiLine = true 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 // unless forceFF is set or there are multiple expressions on // 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 *multiLine = true prevBreak = i @@ -363,7 +351,7 @@ func (p *printer) isOneLineFieldList(list []*ast.Field) bool { 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: - if e.Op.String() == "/" { + if e.Op == token.QUO { // `*/` maxProblem = 5 } diff --git a/libgo/go/go/printer/printer.go b/libgo/go/go/printer/printer.go index a43e4a12c77..697a83fa866 100644 --- a/libgo/go/go/printer/printer.go +++ b/libgo/go/go/printer/printer.go @@ -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 ( esc = []byte{tabwriter.Escape} htab = []byte{'\t'} @@ -81,8 +75,9 @@ type printer struct { mode pmode // current printer mode lastTok token.Token // the last token printed (token.ILLEGAL if it's whitespace) - // Buffered whitespace - buffer []whiteSpace + // Reused buffers + wsbuf []whiteSpace // delayed white space + litbuf bytes.Buffer // for creation of escaped literals and comments // The (possibly estimated) position in the generated output; // 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.fset = fset 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 } @@ -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 // of breaks n such that min <= result <= max where max depends on the current // 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 // the data. // -func (p *printer) writeItem(pos token.Position, data []byte) { +func (p *printer) writeItem(pos token.Position, data string) { if pos.IsValid() { // continue with previous position if we don't have a valid pos 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) p.indent = 0 p.mode = 0 - p.buffer = p.buffer[0:0] + p.wsbuf = p.wsbuf[0:0] } p.pos = pos } @@ -248,7 +257,7 @@ func (p *printer) writeItem(pos token.Position, data []byte) { _, filename := filepath.Split(pos.Filename) p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, pos.Column))) } - p.write(data) + p.write([]byte(data)) p.last = p.pos } @@ -280,11 +289,11 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment if prev == nil { // first comment of a comment group j := 0 - for i, ch := range p.buffer { + for i, ch := range p.wsbuf { switch ch { case blank: // ignore any blanks before a comment - p.buffer[i] = ignore + p.wsbuf[i] = ignore continue case vtab: // respect existing tabs - important @@ -318,11 +327,11 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment if prev == nil { // first comment of a comment group j := 0 - for i, ch := range p.buffer { + for i, ch := range p.wsbuf { switch ch { case blank, vtab: // ignore any horizontal whitespace before line breaks - p.buffer[i] = ignore + p.wsbuf[i] = ignore continue case indent: // apply pending indentation @@ -339,7 +348,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment } case newline, formfeed: // TODO(gri): may want to keep formfeed info in some cases - p.buffer[i] = ignore + p.wsbuf[i] = ignore } j = i 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) { - // line must pass through unchanged, bracket it with tabwriter.Escape - line = bytes.Join([][]byte{esc, line, esc}, nil) - p.writeItem(pos, line) -} - +// TODO(gri): It should be possible to convert the code below from using +// []byte to string and in the process eliminate some conversions. // Split comment text into lines func split(text []byte) [][]byte { @@ -546,13 +551,13 @@ func (p *printer) writeComment(comment *ast.Comment) { // shortcut common case of //-style comments if text[1] == '/' { - p.writeCommentLine(comment, p.fset.Position(comment.Pos()), text) + p.writeItem(p.fset.Position(comment.Pos()), p.escape(text)) return } // for /*-style comments, print line by line and let the // write function take care of the proper indentation - lines := split(text) + lines := split([]byte(text)) stripCommonPrefix(lines) // write comment lines, separated by formfeed, @@ -565,7 +570,7 @@ func (p *printer) writeComment(comment *ast.Comment) { pos = p.pos } 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. // func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) { - for i, ch := range p.buffer { + for i, ch := range p.wsbuf { switch ch { case blank, vtab: // ignore trailing whitespace - p.buffer[i] = ignore + p.wsbuf[i] = ignore case indent, unindent: // don't loose indentation information case newline, formfeed: @@ -594,11 +599,11 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) { if ch == formfeed { 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 if needsLinebreak { @@ -652,7 +657,7 @@ func (p *printer) writeWhitespace(n int) { // write entries var data [1]byte for i := 0; i < n; i++ { - switch ch := p.buffer[i]; ch { + switch ch := p.wsbuf[i]; ch { case ignore: // ignore! case indent: @@ -670,13 +675,13 @@ func (p *printer) writeWhitespace(n int) { // the line break and the label, the unindent is not // part of the comment whitespace prefix and the comment // 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. // Otherwise, a long label name on the next line leading // to a wide column may increase the indentation column // of lines before the label; effectively leading to wrong // indentation. - p.buffer[i], p.buffer[i+1] = unindent, formfeed + p.wsbuf[i], p.wsbuf[i+1] = unindent, formfeed i-- // do it again continue } @@ -689,11 +694,11 @@ func (p *printer) writeWhitespace(n int) { // shift remaining entries down i := 0 - for ; n < len(p.buffer); n++ { - p.buffer[i] = p.buffer[n] + for ; n < len(p.wsbuf); n++ { + p.wsbuf[i] = p.wsbuf[n] 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{}) { for _, f := range args { next := p.pos // estimated position of next item - var data []byte + var data string var tok token.Token switch x := f.(type) { @@ -748,42 +753,22 @@ func (p *printer) print(args ...interface{}) { // LabeledStmt) break } - i := len(p.buffer) - if i == cap(p.buffer) { + i := len(p.wsbuf) + if i == cap(p.wsbuf) { // Whitespace sequences are very short so this should // never happen. Handle gracefully (but possibly with // bad comment placement) if it does happen. p.writeWhitespace(i) i = 0 } - p.buffer = p.buffer[0 : i+1] - p.buffer[i] = x + p.wsbuf = p.wsbuf[0 : i+1] + p.wsbuf[i] = x case *ast.Ident: - data = []byte(x.Name) + data = x.Name tok = token.IDENT case *ast.BasicLit: - // escape all literals so they pass through unchanged - // (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) + data = p.escape(x.Value) 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: s := x.String() if mayCombine(p.lastTok, s[0]) { @@ -793,13 +778,13 @@ func (p *printer) print(args ...interface{}) { // (except for token.INT followed by a '.' this // should never happen because it is taken care // of via binary expression formatting) - if len(p.buffer) != 0 { + if len(p.wsbuf) != 0 { p.internalError("whitespace buffer not empty") } - p.buffer = p.buffer[0:1] - p.buffer[0] = ' ' + p.wsbuf = p.wsbuf[0:1] + p.wsbuf[0] = ' ' } - data = []byte(s) + data = s tok = x case token.Pos: if x.IsValid() { @@ -813,7 +798,7 @@ func (p *printer) print(args ...interface{}) { p.lastTok = tok p.pos = next - if data != nil { + if data != "" { droppedFF := p.flush(next, tok) // 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) } else { // otherwise, write any leftover whitespace - p.writeWhitespace(len(p.buffer)) + p.writeWhitespace(len(p.wsbuf)) } return } @@ -864,10 +849,9 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) { // through unchanged. // type trimmer struct { - output io.Writer - state int - space bytes.Buffer - hasText bool + output io.Writer + state int + space bytes.Buffer } @@ -875,15 +859,11 @@ type trimmer struct { // It can be in one of the following states: const ( inSpace = iota // inside space - atEscape // inside space and the last char was an opening tabwriter.Escape inEscape // inside text bracketed by tabwriter.Escapes inText // inside text ) -var backquote = []byte{'`'} - - // Design note: It is tempting to eliminate extra blanks occurring in // whitespace in this function as it could simplify some // 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) { // invariants: - // p.state == inSpace, atEscape: + // p.state == inSpace: // p.space is unwritten - // p.hasText indicates if there is any text on this line // p.state == inEscape, inText: // data[m:n] is unwritten m := 0 @@ -911,32 +890,20 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) { case '\n', '\f': p.space.Reset() // discard trailing space _, err = p.output.Write(newlines[0:1]) // write newline - p.hasText = false case tabwriter.Escape: - p.state = atEscape + _, err = p.output.Write(p.space.Bytes()) + p.state = inEscape + m = n + 1 // +1: skip tabwriter.Escape default: _, err = p.output.Write(p.space.Bytes()) p.state = inText 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: if b == tabwriter.Escape { _, err = p.output.Write(data[m:n]) p.state = inSpace p.space.Reset() - p.hasText = true } case inText: switch b { @@ -945,19 +912,18 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) { p.state = inSpace p.space.Reset() p.space.WriteByte(b) // WriteByte returns no errors - p.hasText = true case '\n', '\f': _, err = p.output.Write(data[m:n]) p.state = inSpace p.space.Reset() _, err = p.output.Write(newlines[0:1]) // write newline - p.hasText = false case tabwriter.Escape: _, err = p.output.Write(data[m:n]) - p.state = atEscape - p.space.Reset() - p.hasText = true + p.state = inEscape + m = n + 1 // +1: skip tabwriter.Escape } + default: + panic("unreachable") } if err != nil { return @@ -970,7 +936,6 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) { _, err = p.output.Write(data[m:n]) p.state = inSpace p.space.Reset() - p.hasText = true } return diff --git a/libgo/go/go/printer/printer_test.go b/libgo/go/go/printer/printer_test.go index 3ff087e2993..090f92af181 100644 --- a/libgo/go/go/printer/printer_test.go +++ b/libgo/go/go/printer/printer_test.go @@ -114,7 +114,7 @@ func check(t *testing.T, source, golden string, mode checkMode) { // start a timer to produce a time-out signal tc := make(chan int) 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 }() @@ -156,12 +156,15 @@ var data = []entry{ func TestFiles(t *testing.T) { - for _, e := range data { + for i, e := range data { source := filepath.Join(dataDir, e.source) golden := filepath.Join(dataDir, e.golden) check(t, source, golden, e.mode) // 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 + } } } diff --git a/libgo/go/go/printer/testdata/expressions.golden b/libgo/go/go/printer/testdata/expressions.golden index 314d3213c74..c1a7e970b45 100644 --- a/libgo/go/go/printer/testdata/expressions.golden +++ b/libgo/go/go/printer/testdata/expressions.golden @@ -253,8 +253,8 @@ bar` var _ = `` var _ = `foo` var _ = - // the next line should not be indented -`foo + // the next line should remain indented + `foo bar` var _ = // comment @@ -262,8 +262,8 @@ bar` var _ = // comment `foo` var _ = // comment - // the next line should not be indented -`foo + // the next line should remain indented + `foo bar` var _ = /* comment */ `` @@ -276,12 +276,12 @@ bar` var _ = /* comment */ `foo` var _ = /* comment */ - // the next line should not be indented -`foo + // the next line should remain indented + `foo bar` var board = []int( -`........... + `........... ........... ....âââ.... ....âââ.... @@ -296,8 +296,8 @@ bar` var state = S{ "foo", - // the next line should not be indented -`........... + // the next line should remain indented + `........... ........... ....âââ.... ....âââ.... @@ -619,3 +619,13 @@ func _() { b.(T). 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) + } +} diff --git a/libgo/go/go/printer/testdata/expressions.input b/libgo/go/go/printer/testdata/expressions.input index cac22af4313..b87381198e4 100644 --- a/libgo/go/go/printer/testdata/expressions.input +++ b/libgo/go/go/printer/testdata/expressions.input @@ -256,7 +256,7 @@ var _ = var _ = `foo` var _ = - // the next line should not be indented + // the next line should remain indented `foo bar` @@ -266,7 +266,7 @@ bar` var _ = // comment `foo` var _ = // comment - // the next line should not be indented + // the next line should remain indented `foo bar` @@ -282,7 +282,7 @@ bar` var _ = /* comment */ `foo` var _ = /* comment */ - // the next line should not be indented + // the next line should remain indented `foo bar` @@ -304,7 +304,7 @@ var board = []int( var state = S{ "foo", - // the next line should not be indented + // the next line should remain indented `........... ........... ....âââ.... @@ -625,3 +625,13 @@ baz() (T). 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) + } +} diff --git a/libgo/go/go/printer/testdata/expressions.raw b/libgo/go/go/printer/testdata/expressions.raw index f22ceeb476f..735cd943e69 100644 --- a/libgo/go/go/printer/testdata/expressions.raw +++ b/libgo/go/go/printer/testdata/expressions.raw @@ -239,7 +239,8 @@ func _() { _ = `foo bar` _ = `three spaces before the end of the line starting here: -they must not be removed`} +they must not be removed` +} func _() { @@ -252,8 +253,8 @@ bar` var _ = `` var _ = `foo` var _ = - // the next line should not be indented -`foo + // the next line should remain indented + `foo bar` var _ = // comment @@ -261,8 +262,8 @@ bar` var _ = // comment `foo` var _ = // comment - // the next line should not be indented -`foo + // the next line should remain indented + `foo bar` var _ = /* comment */ `` @@ -275,12 +276,12 @@ bar` var _ = /* comment */ `foo` var _ = /* comment */ - // the next line should not be indented -`foo + // the next line should remain indented + `foo bar` var board = []int( -`........... + `........... ........... ....âââ.... ....âââ.... @@ -295,8 +296,8 @@ bar` var state = S{ "foo", - // the next line should not be indented -`........... + // the next line should remain indented + `........... ........... ....âââ.... ....âââ.... @@ -618,3 +619,13 @@ func _() { b.(T). 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) + } +} diff --git a/libgo/go/go/scanner/scanner.go b/libgo/go/go/scanner/scanner.go index 59fed9dffc6..2f949ad2568 100644 --- a/libgo/go/go/scanner/scanner.go +++ b/libgo/go/go/scanner/scanner.go @@ -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 pos, -// the token tok, and the literal text lit corresponding to the +// Scan scans the next token and returns the token position, +// the token, and the literal string corresponding to the // token. The source end is indicated by token.EOF. // // 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 // at EOF. // @@ -560,7 +558,7 @@ var newline = []byte{'\n'} // set with Init. Token positions are relative to that file // 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: S.skipWhitespace() @@ -586,7 +584,7 @@ scanAgain: case -1: if S.insertSemi { S.insertSemi = false // EOF consumed - return S.file.Pos(offs), token.SEMICOLON, newline + return S.file.Pos(offs), token.SEMICOLON, "\n" } tok = token.EOF case '\n': @@ -594,7 +592,7 @@ scanAgain: // set in the first place and exited early // from S.skipWhitespace() S.insertSemi = false // newline consumed - return S.file.Pos(offs), token.SEMICOLON, newline + return S.file.Pos(offs), token.SEMICOLON, "\n" case '"': insertSemi = true tok = token.STRING @@ -662,7 +660,7 @@ scanAgain: S.offset = offs S.rdOffset = offs + 1 S.insertSemi = false // newline consumed - return S.file.Pos(offs), token.SEMICOLON, newline + return S.file.Pos(offs), token.SEMICOLON, "\n" } S.scanComment() if S.mode&ScanComments == 0 { @@ -711,5 +709,9 @@ scanAgain: if S.mode&InsertSemis != 0 { 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]) } diff --git a/libgo/go/go/scanner/scanner_test.go b/libgo/go/go/scanner/scanner_test.go index 93f34581b7f..8afb00ee5bc 100644 --- a/libgo/go/go/scanner/scanner_test.go +++ b/libgo/go/go/scanner/scanner_test.go @@ -234,12 +234,11 @@ func TestScan(t *testing.T) { index := 0 epos := token.Position{"", 0, 1, 1} // expected position for { - pos, tok, litb := s.Scan() + pos, tok, lit := s.Scan() e := elt{token.EOF, "", special} if index < len(tokens) { e = tokens[index] } - lit := string(litb) if tok == token.EOF { lit = "" epos.Line = src_linecount @@ -257,7 +256,7 @@ func TestScan(t *testing.T) { } epos.Offset += len(lit) + len(whitespace) 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 epos.Offset++ epos.Line++ @@ -292,7 +291,7 @@ func checkSemi(t *testing.T, line string, mode uint) { semiPos.Column++ pos, tok, lit = S.Scan() if tok == token.SEMICOLON { - if string(lit) != semiLit { + if lit != semiLit { t.Errorf(`bad literal for %q: got %q, expected %q`, line, lit, semiLit) } checkPos(t, line, pos, semiPos) @@ -493,7 +492,7 @@ func TestLineComments(t *testing.T) { for _, s := range segments { p, _, lit := S.Scan() 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 { @@ -547,10 +546,10 @@ func TestIllegalChars(t *testing.T) { for offs, ch := range src { pos, tok, lit := s.Scan() 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) { - t.Errorf("bad token: got %s, expected %s", string(lit), string(ch)) + if tok == token.ILLEGAL && lit != string(ch) { + t.Errorf("bad token: got %s, expected %s", lit, string(ch)) } } diff --git a/libgo/go/go/token/token.go b/libgo/go/go/token/token.go index 2a2d3ecc4fb..a5f21df168e 100644 --- a/libgo/go/go/token/token.go +++ b/libgo/go/go/token/token.go @@ -126,10 +126,7 @@ const ( ) -// At the moment we have no array literal syntax that lets us describe -// the index for each element - use a map for now to make sure they are -// in sync. -var tokens = map[Token]string{ +var tokens = [...]string{ ILLEGAL: "ILLEGAL", EOF: "EOF", @@ -237,10 +234,14 @@ var tokens = map[Token]string{ // constant name (e.g. for the token IDENT, the string is "IDENT"). // func (tok Token) String() string { - if str, exists := tokens[tok]; exists { - return str + s := "" + 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 } diff --git a/libgo/go/go/typechecker/scope.go b/libgo/go/go/typechecker/scope.go index bd24f4ca42d..a4bee6e6962 100644 --- a/libgo/go/go/typechecker/scope.go +++ b/libgo/go/go/typechecker/scope.go @@ -33,7 +33,7 @@ func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.ObjKind, name *ast //obj.N = n name.Obj = obj 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()) } } diff --git a/libgo/go/go/typechecker/typechecker.go b/libgo/go/go/typechecker/typechecker.go index 4fc5647f0d5..b5e695d973a 100644 --- a/libgo/go/go/typechecker/typechecker.go +++ b/libgo/go/go/typechecker/typechecker.go @@ -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 { // 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) } @@ -327,8 +327,8 @@ func (tc *typechecker) checkBlock(body []ast.Stmt, ftype *Type) { if ftype != nil { for _, par := range ftype.Params.Objects { if par.Name != "_" { - obj := tc.topScope.Insert(par) - assert(obj == par) // ftype has no double declarations + alt := tc.topScope.Insert(par) + assert(alt == nil) // ftype has no double declarations } } } diff --git a/libgo/go/go/typechecker/typechecker_test.go b/libgo/go/go/typechecker/typechecker_test.go index 3988ff1680b..d16e0692180 100644 --- a/libgo/go/go/typechecker/typechecker_test.go +++ b/libgo/go/go/typechecker/typechecker_test.go @@ -78,7 +78,7 @@ func expectedErrors(t *testing.T, pkg *ast.Package) (list scanner.ErrorList) { case token.EOF: break loop case token.COMMENT: - s := errRx.FindSubmatch(lit) + s := errRx.FindStringSubmatch(lit) if len(s) == 2 { list = append(list, &scanner.Error{fset.Position(prev), string(s[1])}) } diff --git a/libgo/go/go/typechecker/universe.go b/libgo/go/go/typechecker/universe.go index cf4434993e1..abc8bbbd49c 100644 --- a/libgo/go/go/typechecker/universe.go +++ b/libgo/go/go/typechecker/universe.go @@ -14,7 +14,7 @@ var Universe *ast.Scope func def(obj *ast.Object) { alt := Universe.Insert(obj) - if alt != obj { + if alt != nil { panic("object declared twice") } } diff --git a/libgo/go/gob/type.go b/libgo/go/gob/type.go index fc620f5c7c1..305d41980a5 100644 --- a/libgo/go/gob/type.go +++ b/libgo/go/gob/type.go @@ -407,7 +407,7 @@ func (s *structType) string() string { return s.safeString(make(map[typeId]bool) func newStructType(name string) *structType { s := &structType{CommonType{Name: name}, nil} // 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) 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. // Should be called only when handling GobEncoders/Decoders, // 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. func getType(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error) { 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) { 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()) } } diff --git a/libgo/go/http/fs.go b/libgo/go/http/fs.go index 4ad680ccc31..8b5c4770c49 100644 --- a/libgo/go/http/fs.go +++ b/libgo/go/http/fs.go @@ -154,7 +154,10 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { // handle Content-Range header. // TODO(adg): handle multiple ranges 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) return } diff --git a/libgo/go/http/pprof/pprof.go b/libgo/go/http/pprof/pprof.go index 0bac26687d7..bc79e218320 100644 --- a/libgo/go/http/pprof/pprof.go +++ b/libgo/go/http/pprof/pprof.go @@ -18,6 +18,10 @@ // // 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 import ( @@ -29,10 +33,12 @@ import ( "runtime/pprof" "strconv" "strings" + "time" ) func init() { 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/symbol", http.HandlerFunc(Symbol)) } @@ -41,22 +47,46 @@ func init() { // command line, with arguments separated by NUL bytes. // The package initialization registers it as /debug/pprof/cmdline. 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")) } // Heap responds with the pprof-formatted heap profile. // The package initialization registers it as /debug/pprof/heap. 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) } +// 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, // responding with a table mapping program counters to function names. // The package initialization registers it as /debug/pprof/symbol. 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 // do have symbol information. Pprof only cares whether diff --git a/libgo/go/http/serve_test.go b/libgo/go/http/serve_test.go index 683de85b867..b0e26e53359 100644 --- a/libgo/go/http/serve_test.go +++ b/libgo/go/http/serve_test.go @@ -175,7 +175,7 @@ func TestHostHandlers(t *testing.T) { ts := httptest.NewServer(nil) defer ts.Close() - conn, err := net.Dial("tcp", "", ts.Listener.Addr().String()) + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) if err != nil { t.Fatal(err) } @@ -265,7 +265,7 @@ func TestServerTimeouts(t *testing.T) { // Slow client that should timeout. 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 { 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 // 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 { t.Fatalf("error dialing: %v", err) } @@ -377,7 +377,7 @@ func TestServeHTTP10Close(t *testing.T) { })) defer s.Close() - conn, err := net.Dial("tcp", "", s.Listener.Addr().String()) + conn, err := net.Dial("tcp", s.Listener.Addr().String()) if err != nil { t.Fatal("dial error:", err) } diff --git a/libgo/go/http/transport.go b/libgo/go/http/transport.go index 8a73ead31f9..ed7843bc713 100644 --- a/libgo/go/http/transport.go +++ b/libgo/go/http/transport.go @@ -195,7 +195,7 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, os.Error) { return pc, nil } - conn, err := net.Dial("tcp", "", cm.addr()) + conn, err := net.Dial("tcp", cm.addr()) if err != nil { return nil, err } diff --git a/libgo/go/image/png/reader_test.go b/libgo/go/image/png/reader_test.go index 8314a833877..0b2058d51a2 100644 --- a/libgo/go/image/png/reader_test.go +++ b/libgo/go/image/png/reader_test.go @@ -34,6 +34,12 @@ var filenames = []string{ "basn6a16", } +var filenamesShort = []string{ + "basn0g01", + "basn0g04-31", + "basn6a16", +} + func readPng(filename string) (image.Image, os.Error) { f, err := os.Open(filename, os.O_RDONLY, 0444) if err != nil { @@ -157,7 +163,11 @@ func sng(w io.WriteCloser, filename string, png image.Image) { } func TestReader(t *testing.T) { - for _, fn := range filenames { + names := filenames + if testing.Short() { + names = filenamesShort + } + for _, fn := range names { // Read the .png file. img, err := readPng("testdata/pngsuite/" + fn + ".png") if err != nil { diff --git a/libgo/go/image/png/writer_test.go b/libgo/go/image/png/writer_test.go index f218a5564bb..4d9929f314f 100644 --- a/libgo/go/image/png/writer_test.go +++ b/libgo/go/image/png/writer_test.go @@ -32,7 +32,11 @@ func diff(m0, m1 image.Image) os.Error { func TestWriter(t *testing.T) { // 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" // Read the image. m0, err := readPng(qfn) diff --git a/libgo/go/json/decode_test.go b/libgo/go/json/decode_test.go index ad6026363b5..aad8b635f2b 100644 --- a/libgo/go/json/decode_test.go +++ b/libgo/go/json/decode_test.go @@ -157,6 +157,7 @@ func TestUnmarshal(t *testing.T) { } func TestUnmarshalMarshal(t *testing.T) { + initBig() var v interface{} if err := Unmarshal(jsonBig, &v); err != nil { t.Fatalf("Unmarshal: %v", err) diff --git a/libgo/go/json/scanner_test.go b/libgo/go/json/scanner_test.go index 2dc8ff87fb4..0d4de3246dd 100644 --- a/libgo/go/json/scanner_test.go +++ b/libgo/go/json/scanner_test.go @@ -85,6 +85,7 @@ func TestIndent(t *testing.T) { // Tests of a large random structure. func TestCompactBig(t *testing.T) { + initBig() var buf bytes.Buffer if err := Compact(&buf, jsonBig); err != nil { t.Fatalf("Compact: %v", err) @@ -98,6 +99,7 @@ func TestCompactBig(t *testing.T) { } func TestIndentBig(t *testing.T) { + initBig() var buf bytes.Buffer if err := Indent(&buf, jsonBig, "", "\t"); err != nil { t.Fatalf("Indent1: %v", err) @@ -135,6 +137,7 @@ func TestIndentBig(t *testing.T) { } func TestNextValueBig(t *testing.T) { + initBig() var scan scanner item, rest, err := nextValue(jsonBig, &scan) if err != nil { @@ -160,6 +163,7 @@ func TestNextValueBig(t *testing.T) { } func BenchmarkSkipValue(b *testing.B) { + initBig() var scan scanner for i := 0; i < b.N; i++ { nextValue(jsonBig, &scan) @@ -191,12 +195,23 @@ func trim(b []byte) []byte { var jsonBig []byte -func init() { - b, err := Marshal(genValue(10000)) - if err != nil { - panic(err) +const ( + big = 10000 + small = 100 +) + +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{} { diff --git a/libgo/go/net/cgo_stub.go b/libgo/go/net/cgo_stub.go new file mode 100644 index 00000000000..e28f6622e93 --- /dev/null +++ b/libgo/go/net/cgo_stub.go @@ -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 +} diff --git a/libgo/go/net/dial.go b/libgo/go/net/dial.go index 1cf8e79159e..66cb09b19bb 100644 --- a/libgo/go/net/dial.go +++ b/libgo/go/net/dial.go @@ -6,9 +6,7 @@ package net import "os" -// Dial connects to the remote address raddr on the network net. -// If the string laddr is not empty, it is used as the local address -// for the connection. +// Dial connects to the address addr on the network net. // // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), // "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 // a literal IPv6 address, it must be enclosed in square brackets. +// The functions JoinHostPort and SplitHostPort manipulate +// addresses in this form. // // Examples: -// Dial("tcp", "", "12.34.56.78:80") -// Dial("tcp", "", "google.com:80") -// Dial("tcp", "", "[de:ad:be:ef::ca:fe]:80") -// Dial("tcp", "127.0.0.1:123", "127.0.0.1:88") +// Dial("tcp", "12.34.56.78:80") +// Dial("tcp", "google.com:80") +// Dial("tcp", "[de:ad:be:ef::ca:fe]:80") // -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 { case "tcp", "tcp4", "tcp6": - var la, ra *TCPAddr - if laddr != "" { - if la, err = ResolveTCPAddr(laddr); err != nil { - goto Error - } - } - if raddr != "" { - if ra, err = ResolveTCPAddr(raddr); err != nil { - goto Error - } + var ra *TCPAddr + if ra, err = ResolveTCPAddr(raddr); err != nil { + goto Error } - c, err := DialTCP(net, la, ra) + c, err := DialTCP(net, nil, ra) if err != nil { return nil, err } return c, nil case "udp", "udp4", "udp6": - var la, ra *UDPAddr - if laddr != "" { - if la, err = ResolveUDPAddr(laddr); err != nil { - goto Error - } - } - if raddr != "" { - if ra, err = ResolveUDPAddr(raddr); err != nil { - goto Error - } + var ra *UDPAddr + if ra, err = ResolveUDPAddr(raddr); err != nil { + goto Error } - c, err := DialUDP(net, la, ra) + c, err := DialUDP(net, nil, ra) if err != nil { return nil, err } return c, nil case "unix", "unixgram", "unixpacket": - var la, ra *UnixAddr - if raddr != "" { - if ra, err = ResolveUnixAddr(net, raddr); err != nil { - goto Error - } - } - if laddr != "" { - if la, err = ResolveUnixAddr(net, laddr); err != nil { - goto Error - } + var ra *UnixAddr + if ra, err = ResolveUnixAddr(net, raddr); err != nil { + goto Error } - c, err = DialUnix(net, la, ra) + c, err = DialUnix(net, nil, ra) if err != nil { return nil, err } return c, nil case "ip", "ip4", "ip6": - var la, ra *IPAddr - if laddr != "" { - if la, err = ResolveIPAddr(laddr); err != nil { - goto Error - } - } - if raddr != "" { - if ra, err = ResolveIPAddr(raddr); err != nil { - goto Error - } + var ra *IPAddr + if ra, err = ResolveIPAddr(raddr); err != nil { + goto Error } - c, err := DialIP(net, la, ra) + c, err := DialIP(net, nil, ra) if err != nil { return nil, err } diff --git a/libgo/go/net/dialgoogle_test.go b/libgo/go/net/dialgoogle_test.go index a432800cfe2..9a9c02ebd71 100644 --- a/libgo/go/net/dialgoogle_test.go +++ b/libgo/go/net/dialgoogle_test.go @@ -32,7 +32,7 @@ func fetchGoogle(t *testing.T, fd Conn, network, addr string) { } func doDial(t *testing.T, network, addr string) { - fd, err := Dial(network, "", addr) + fd, err := Dial(network, addr) if err != nil { t.Errorf("Dial(%q, %q, %q) = _, %v", network, "", addr, err) return @@ -55,6 +55,13 @@ var googleaddrs = []string{ "[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) { // If no ipv6 tunnel, don't try the last address. if !*ipv6 { @@ -64,14 +71,14 @@ func TestDialGoogle(t *testing.T) { // Insert an actual IP address for google.com // into the table. - _, addrs, err := LookupHost("www.google.com") + addrs, err := LookupIP("www.google.com") if err != nil { t.Fatalf("lookup www.google.com: %v", err) } if len(addrs) == 0 { t.Fatalf("no addresses for www.google.com") } - ip := ParseIP(addrs[0]).To4() + ip := addrs[0].To4() for i, s := range googleaddrs { if strings.Contains(s, "%") { diff --git a/libgo/go/net/dnsclient.go b/libgo/go/net/dnsclient.go index 3252dd45406..32cea6125eb 100644 --- a/libgo/go/net/dnsclient.go +++ b/libgo/go/net/dnsclient.go @@ -159,7 +159,7 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs // all the cfg.servers[i] are IP addresses, which // Dial will use without a DNS lookup. server := cfg.servers[i] + ":53" - c, cerr := Dial("udp", "", server) + c, cerr := Dial("udp", server) if cerr != nil { err = cerr continue @@ -178,12 +178,23 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs return } -func convertRR_A(records []dnsRR) []string { - addrs := make([]string, len(records)) +func convertRR_A(records []dnsRR) []IP { + addrs := make([]IP, len(records)) for i := 0; i < len(records); i++ { rr := records[i] 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 } @@ -294,10 +305,8 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Erro return } -// LookupHost looks for name using the local hosts file and DNS resolver. -// It returns the canonical name for the host and an array of that -// host's addresses. -func LookupHost(name string) (cname string, addrs []string, err os.Error) { +// goLookupHost is the native Go implementation of LookupHost. +func goLookupHost(name string) (addrs []string, err os.Error) { onceLoadConfig.Do(loadConfig) if dnserr != nil || cfg == nil { 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. addrs = lookupStaticHost(name) 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 } var records []dnsRR + var cname string cname, records, err = lookup(name, dnsTypeA) if err != nil { return } 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 +} + +// 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 { Target string Port uint16 @@ -344,11 +404,13 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os. return } +// An MX represents a single DNS MX record. type MX struct { Host string Pref uint16 } +// LookupMX returns the DNS MX records associated with name. func LookupMX(name string) (entries []*MX, err os.Error) { var records []dnsRR _, records, err = lookup(name, dnsTypeMX) diff --git a/libgo/go/net/dnsmsg.go b/libgo/go/net/dnsmsg.go index dc195caf801..5209c1a06a5 100644 --- a/libgo/go/net/dnsmsg.go +++ b/libgo/go/net/dnsmsg.go @@ -50,6 +50,7 @@ const ( dnsTypeMINFO = 14 dnsTypeMX = 15 dnsTypeTXT = 16 + dnsTypeAAAA = 28 dnsTypeSRV = 33 // valid dnsQuestion.qtype only @@ -244,8 +245,18 @@ type dnsRR_A struct { 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. // @@ -270,6 +281,7 @@ var rr_mk = map[int]func() dnsRR{ dnsTypeTXT: func() dnsRR { return new(dnsRR_TXT) }, dnsTypeSRV: func() dnsRR { return new(dnsRR_SRV) }, dnsTypeA: func() dnsRR { return new(dnsRR_A) }, + dnsTypeAAAA: func() dnsRR { return new(dnsRR_AAAA) }, } // Pack a domain name s into msg[off:]. @@ -377,7 +389,7 @@ Loop: // TODO(rsc): Move into generic library? // 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) { for i := 0; i < val.NumField(); 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) 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: // There are multiple string encodings. // 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)) 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: var s string switch f.Tag { @@ -515,7 +547,8 @@ func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { // Generic struct printer. // 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. func printStructValue(val *reflect.StructValue) string { s := "{" @@ -533,6 +566,9 @@ func printStructValue(val *reflect.StructValue) string { } else if fv, ok := fval.(*reflect.UintValue); ok && f.Tag == "ipv4" { i := fv.Get() 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 { s += fmt.Sprint(fval.Interface()) } diff --git a/libgo/go/net/fd.go b/libgo/go/net/fd.go index c8440ae484a..7e4b83a9f99 100644 --- a/libgo/go/net/fd.go +++ b/libgo/go/net/fd.go @@ -274,19 +274,25 @@ func startServer() { 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) if e := syscall.SetNonblock(fd, true); e != 0 { - return nil, &OpError{"setnonblock", net, laddr, os.Errno(e)} + return nil, os.Errno(e) } f = &netFD{ sysfd: fd, family: family, proto: proto, 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 if laddr != nil { ls = laddr.String() @@ -294,10 +300,23 @@ func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err if raddr != nil { rs = raddr.String() } - f.sysfile = os.NewFile(fd, net+":"+ls+"->"+rs) - f.cr = make(chan bool, 1) - f.cw = make(chan bool, 1) - return f, nil + fd.sysfile = os.NewFile(fd.sysfd, fd.net+":"+ls+"->"+rs) +} + +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. @@ -593,10 +612,11 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os. syscall.CloseOnExec(s) 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) return nil, err } + nfd.setAddr(fd.laddr, toAddr(sa)) return nfd, nil } diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go index 63a8fbc4489..c2f736cc126 100644 --- a/libgo/go/net/fd_windows.go +++ b/libgo/go/net/fd_windows.go @@ -225,29 +225,40 @@ type netFD struct { 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{ sysfd: fd, family: family, proto: proto, net: net, - laddr: laddr, - raddr: raddr, } runtime.SetFinalizer(f, (*netFD).Close) 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 { return nil, initErr } onceStartServer.Do(startServer) // Associate our socket with resultsrv.iocp. 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), 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 allocFD(fd, family, proto, net, laddr, raddr), nil + return nil } // 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() 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. diff --git a/libgo/go/net/file.go b/libgo/go/net/file.go new file mode 100644 index 00000000000..0e411a192f2 --- /dev/null +++ b/libgo/go/net/file.go @@ -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 +} diff --git a/libgo/go/net/file_test.go b/libgo/go/net/file_test.go new file mode 100644 index 00000000000..1ec05fdeea5 --- /dev/null +++ b/libgo/go/net/file_test.go @@ -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") + } +} diff --git a/libgo/go/net/file_windows.go b/libgo/go/net/file_windows.go new file mode 100644 index 00000000000..94aa583755b --- /dev/null +++ b/libgo/go/net/file_windows.go @@ -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) +} diff --git a/libgo/go/net/hosts_test.go b/libgo/go/net/hosts_test.go index 84cd92e376c..470e35f7863 100644 --- a/libgo/go/net/hosts_test.go +++ b/libgo/go/net/hosts_test.go @@ -13,7 +13,6 @@ type hostTest struct { ips []IP } - var hosttests = []hostTest{ {"odin", []IP{ IPv4(127, 0, 0, 2), diff --git a/libgo/go/net/ip.go b/libgo/go/net/ip.go index 1904af0d6ad..12bb6f351a1 100644 --- a/libgo/go/net/ip.go +++ b/libgo/go/net/ip.go @@ -474,13 +474,13 @@ func parseIPv6(s string) IP { return p } -// A SyntaxError represents a malformed text string and the type of string that was expected. -type SyntaxError struct { +// A ParseError represents a malformed text string and the type of string that was expected. +type ParseError struct { Type string Text string } -func (e *SyntaxError) String() string { +func (e *ParseError) String() string { 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, -// 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) { i := byteIndex(s, '/') if i < 0 { - return nil, nil, &SyntaxError{"CIDR address", s} + return nil, nil, &ParseError{"CIDR address", s} } 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) - if ip == nil || !ok || i != len(maskstr) || nn < 0 || nn > 8*len(ip) { - return nil, nil, &SyntaxError{"CIDR address", s} + if ip == nil || !ok || i != len(maskstr) || nn < 0 || nn > 8*iplen { + return nil, nil, &ParseError{"CIDR address", s} } n := uint(nn) - if len(ip) == 4 { + if iplen == 4 { v4mask := ^uint32(0xffffffff >> n) - mask = IPMask(IPv4(byte(v4mask>>24), byte(v4mask>>16), byte(v4mask>>8), byte(v4mask))) - return ip, mask, nil - } - mask = make(IPMask, 16) - for i := 0; i < 16; i++ { - if n >= 8 { - mask[i] = 0xff - n -= 8 - continue + mask = IPv4Mask(byte(v4mask>>24), byte(v4mask>>16), byte(v4mask>>8), byte(v4mask)) + } else { + mask = make(IPMask, 16) + for i := 0; i < 16; i++ { + if n >= 8 { + mask[i] = 0xff + n -= 8 + 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 } diff --git a/libgo/go/net/ip_test.go b/libgo/go/net/ip_test.go index e29c3021da9..f1a4716d227 100644 --- a/libgo/go/net/ip_test.go +++ b/libgo/go/net/ip_test.go @@ -5,30 +5,26 @@ package net import ( + "bytes" + "reflect" "testing" + "os" ) -func isEqual(a, b IP) bool { +func isEqual(a, b []byte) bool { if a == nil && b == nil { return true } - if a == nil || b == nil || len(a) != len(b) { + if a == nil || b == nil { return false } - for i := 0; i < len(a); i++ { - if a[i] != b[i] { - return false - } - } - return true + return bytes.Equal(a, b) } -type parseIPTest struct { +var parseiptests = []struct { in string out IP -} - -var parseiptests = []parseIPTest{ +}{ {"127.0.1.2", IPv4(127, 0, 1, 2)}, {"127.0.0.1", IPv4(127, 0, 0, 1)}, {"127.0.0.256", nil}, @@ -43,20 +39,17 @@ var parseiptests = []parseIPTest{ } func TestParseIP(t *testing.T) { - for i := 0; i < len(parseiptests); i++ { - tt := parseiptests[i] + for _, tt := range parseiptests { if out := ParseIP(tt.in); !isEqual(out, tt.out) { t.Errorf("ParseIP(%#q) = %v, want %v", tt.in, out, tt.out) } } } -type ipStringTest struct { +var ipstringtests = []struct { in IP out string -} - -var ipstringtests = []ipStringTest{ +}{ // cf. RFC 5952 (A Recommendation for IPv6 Address Text Representation) {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1}, @@ -85,10 +78,67 @@ var ipstringtests = []ipStringTest{ } func TestIPString(t *testing.T) { - for i := 0; i < len(ipstringtests); i++ { - tt := ipstringtests[i] + for _, tt := range ipstringtests { if out := tt.in.String(); 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) + } + } +} diff --git a/libgo/go/net/iprawsock.go b/libgo/go/net/iprawsock.go index 81a918ce5cb..60433303ae1 100644 --- a/libgo/go/net/iprawsock.go +++ b/libgo/go/net/iprawsock.go @@ -240,7 +240,7 @@ func hostToIP(host string) (ip IP, err os.Error) { addr = ParseIP(host) if addr == nil { // Not an IP address. Try as a DNS name. - _, addrs, err1 := LookupHost(host) + addrs, err1 := LookupHost(host) if err1 != nil { err = err1 goto Error diff --git a/libgo/go/net/ipsock.go b/libgo/go/net/ipsock.go index ae4204b48aa..80bc3eea5da 100644 --- a/libgo/go/net/ipsock.go +++ b/libgo/go/net/ipsock.go @@ -170,9 +170,10 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { return nil, InvalidAddrError("unexpected socket family") } -// Split "host:port" into "host" and "port". -// Host cannot contain colons unless it is bracketed. -func splitHostPort(hostport string) (host, port string, err os.Error) { +// SplitHostPort splits a network address of the form +// "host:port" or "[host]:port" into host and port. +// 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. i := last(hostport, ':') if i < 0 { @@ -195,9 +196,9 @@ func splitHostPort(hostport string) (host, port string, err os.Error) { return } -// Join "host" and "port" into "host:port". -// If host contains colons, will join into "[host]:port". -func joinHostPort(host, port string) string { +// JoinHostPort combines host and port into a network address +// of the form "host:port" or, if host contains a colon, "[host]:port". +func JoinHostPort(host, port string) string { // If host has colons, have to bracket it. if byteIndex(host, ':') >= 0 { return "[" + host + "]:" + port @@ -207,7 +208,7 @@ func joinHostPort(host, port string) string { // Convert "host:port" into IP address and port. 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 { goto Error } @@ -218,7 +219,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) { addr = ParseIP(host) if addr == nil { // Not an IP address. Try as a DNS name. - _, addrs, err1 := LookupHost(host) + addrs, err1 := LookupHost(host) if err1 != nil { err = err1 goto Error diff --git a/libgo/go/net/lookup.go b/libgo/go/net/lookup.go new file mode 100644 index 00000000000..7b2185ed419 --- /dev/null +++ b/libgo/go/net/lookup.go @@ -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 +} diff --git a/libgo/go/net/net_test.go b/libgo/go/net/net_test.go index 275b31c0e31..da7928351ce 100644 --- a/libgo/go/net/net_test.go +++ b/libgo/go/net/net_test.go @@ -15,50 +15,49 @@ var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check f type DialErrorTest struct { Net string - Laddr string Raddr string Pattern string } var dialErrorTests = []DialErrorTest{ { - "datakit", "", "mh/astro/r70", + "datakit", "mh/astro/r70", "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/âº", }, { - "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 (.*)", }, { - "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 (.*)", }, { - "tcp", "", "no-such-name:80", + "tcp", "no-such-name:80", `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", }, { - "unix", "", "/etc/file-not-found", + "unix", "/etc/file-not-found", "dial unix /etc/file-not-found: [nN]o such file or directory", }, { - "unix", "", "/etc/", - "dial unix /etc/: ([pP]ermission denied|[sS]ocket operation on non-socket|[cC]onnection refused)", + "unix", "/etc/", + "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", }, { - "unixpacket", "", "/etc/", + "unixpacket", "/etc/", "dial unixpacket /etc/: (permission denied|socket operation on non-socket|connection refused)", }, } @@ -69,7 +68,7 @@ func TestDialError(t *testing.T) { return } for i, tt := range dialErrorTests { - c, e := Dial(tt.Net, tt.Laddr, tt.Raddr) + c, e := Dial(tt.Net, tt.Raddr) if c != nil { c.Close() } diff --git a/libgo/go/net/port.go b/libgo/go/net/port.go index 7d25058b29c..8f8327a3733 100644 --- a/libgo/go/net/port.go +++ b/libgo/go/net/port.go @@ -50,8 +50,8 @@ func readServices() { file.close() } -// LookupPort looks up the port for the given network and service. -func LookupPort(network, service string) (port int, err os.Error) { +// goLookupPort is the native Go implementation of LookupPort. +func goLookupPort(network, service string) (port int, err os.Error) { onceReadServices.Do(readServices) switch network { diff --git a/libgo/go/net/server_test.go b/libgo/go/net/server_test.go index 3dda500e585..37695a068d1 100644 --- a/libgo/go/net/server_test.go +++ b/libgo/go/net/server_test.go @@ -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) { - var laddr string + var fd Conn + var err os.Error 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 { - 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 diff --git a/libgo/go/net/sock.go b/libgo/go/net/sock.go index 5a88ddcbc29..933700af160 100644 --- a/libgo/go/net/sock.go +++ b/libgo/go/net/sock.go @@ -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 { - e = syscall.Connect(s, ra) - for e == syscall.EINTR { - e = syscall.Connect(s, ra) - } - if e != 0 { + if err = fd.connect(ra); err != nil { + fd.sysfd = -1 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) raddr := toAddr(sa) - fd, err = newFD(s, f, p, net, laddr, raddr) - if err != nil { - closesocket(s) - return nil, err - } - + fd.setAddr(laddr, raddr) return fd, nil } @@ -170,9 +167,9 @@ func (e *UnknownSocketError) String() string { func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) { switch a := sa.(type) { 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: - 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: return a.Name, nil } diff --git a/libgo/go/net/tcpsock.go b/libgo/go/net/tcpsock.go index a4bca11bb48..b484be20b46 100644 --- a/libgo/go/net/tcpsock.go +++ b/libgo/go/net/tcpsock.go @@ -34,7 +34,7 @@ func (a *TCPAddr) String() string { if a == nil { return " " } - return joinHostPort(a.IP.String(), itoa(a.Port)) + return JoinHostPort(a.IP.String(), itoa(a.Port)) } 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. 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 -// and returns a TCPConn structure. +// DialTCP connects to the remote address raddr on the network net, +// 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) { if raddr == nil { return nil, &OpError{"dial", "tcp", nil, errMissingAddress} diff --git a/libgo/go/net/textproto/textproto.go b/libgo/go/net/textproto/textproto.go index f62009c523b..fbfad9d61ce 100644 --- a/libgo/go/net/textproto/textproto.go +++ b/libgo/go/net/textproto/textproto.go @@ -78,7 +78,7 @@ func (c *Conn) Close() os.Error { // Dial connects to the given address on the given network using net.Dial // and then returns a new Conn for the connection. func Dial(network, addr string) (*Conn, os.Error) { - c, err := net.Dial(network, "", addr) + c, err := net.Dial(network, addr) if err != nil { return nil, err } diff --git a/libgo/go/net/timeout_test.go b/libgo/go/net/timeout_test.go index 09a257dc817..0dbab5846a6 100644 --- a/libgo/go/net/timeout_test.go +++ b/libgo/go/net/timeout_test.go @@ -11,7 +11,7 @@ import ( ) func testTimeout(t *testing.T, network, addr string, readFrom bool) { - fd, err := Dial(network, "", addr) + fd, err := Dial(network, addr) if err != nil { t.Errorf("dial %s %s failed: %v", network, addr, err) return diff --git a/libgo/go/net/udpsock.go b/libgo/go/net/udpsock.go index f9274493e60..44d618dab08 100644 --- a/libgo/go/net/udpsock.go +++ b/libgo/go/net/udpsock.go @@ -34,7 +34,7 @@ func (a *UDPAddr) String() string { if a == nil { return " " } - return joinHostPort(a.IP.String(), itoa(a.Port)) + return JoinHostPort(a.IP.String(), itoa(a.Port)) } func (a *UDPAddr) family() int { diff --git a/libgo/go/netchan/import.go b/libgo/go/netchan/import.go index 5db679a3ed6..8ba5df9a515 100644 --- a/libgo/go/netchan/import.go +++ b/libgo/go/netchan/import.go @@ -48,7 +48,7 @@ func NewImporter(conn io.ReadWriter) *Importer { // Import imports a set of channels from the given network and address. func Import(network, remoteaddr string) (*Importer, os.Error) { - conn, err := net.Dial(network, "", remoteaddr) + conn, err := net.Dial(network, remoteaddr) if err != nil { return nil, err } diff --git a/libgo/go/netchan/netchan_test.go b/libgo/go/netchan/netchan_test.go index 1b5c560872e..fd4d8f780d9 100644 --- a/libgo/go/netchan/netchan_test.go +++ b/libgo/go/netchan/netchan_test.go @@ -399,7 +399,7 @@ func TestImportFlowControl(t *testing.T) { func testFlow(sendDone chan bool, ch <-chan int, N int, t *testing.T) { go func() { - time.Sleep(1e9) + time.Sleep(0.5e9) sendDone <- false }() diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go index 57d4a477fc6..01268e53a78 100644 --- a/libgo/go/os/file_unix.go +++ b/libgo/go/os/file_unix.go @@ -108,3 +108,21 @@ func Truncate(name string, size int64) Error { } 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 +} diff --git a/libgo/go/rpc/client.go b/libgo/go/rpc/client.go index 92372521175..8af4afcf697 100644 --- a/libgo/go/rpc/client.go +++ b/libgo/go/rpc/client.go @@ -208,7 +208,7 @@ func DialHTTP(network, address string) (*Client, os.Error) { // at the specified network address and path. func DialHTTPPath(network, address, path string) (*Client, os.Error) { var err os.Error - conn, err := net.Dial(network, "", address) + conn, err := net.Dial(network, address) if err != nil { 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. func Dial(network, address string) (*Client, os.Error) { - conn, err := net.Dial(network, "", address) + conn, err := net.Dial(network, address) if err != nil { return nil, err } diff --git a/libgo/go/rpc/jsonrpc/client.go b/libgo/go/rpc/jsonrpc/client.go index 5b806bd6e25..57e977d3253 100644 --- a/libgo/go/rpc/jsonrpc/client.go +++ b/libgo/go/rpc/jsonrpc/client.go @@ -116,7 +116,7 @@ func NewClient(conn io.ReadWriteCloser) *rpc.Client { // Dial connects to a JSON-RPC server at the specified network address. func Dial(network, address string) (*rpc.Client, os.Error) { - conn, err := net.Dial(network, "", address) + conn, err := net.Dial(network, address) if err != nil { return nil, err } diff --git a/libgo/go/runtime/pprof/pprof_test.go b/libgo/go/runtime/pprof/pprof_test.go index 603465eaa52..a060917a280 100644 --- a/libgo/go/runtime/pprof/pprof_test.go +++ b/libgo/go/runtime/pprof/pprof_test.go @@ -15,7 +15,15 @@ import ( ) 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 } diff --git a/libgo/go/smtp/smtp.go b/libgo/go/smtp/smtp.go index 2f6d2f31a78..3f89af14720 100644 --- a/libgo/go/smtp/smtp.go +++ b/libgo/go/smtp/smtp.go @@ -39,7 +39,7 @@ type Client struct { // Dial returns a new Client connected to an SMTP server at addr. func Dial(addr string) (*Client, os.Error) { - conn, err := net.Dial("tcp", "", addr) + conn, err := net.Dial("tcp", addr) if err != nil { return nil, err } diff --git a/libgo/go/sort/sort_test.go b/libgo/go/sort/sort_test.go index 1bea8f03262..3d7337fd010 100644 --- a/libgo/go/sort/sort_test.go +++ b/libgo/go/sort/sort_test.go @@ -74,7 +74,11 @@ func TestSortStrings(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++ { data[i] = rand.Intn(100) } @@ -174,6 +178,9 @@ func lg(n int) int { func TestBentleyMcIlroy(t *testing.T) { sizes := []int{100, 1023, 1024, 1025} + if testing.Short() { + sizes = []int{100, 127, 128, 129} + } dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"} modes := []string{"copy", "reverse", "reverse1", "reverse2", "sort", "dither"} var tmp1, tmp2 [1025]int diff --git a/libgo/go/strings/strings.go b/libgo/go/strings/strings.go index 5f009e54859..93c7c464738 100644 --- a/libgo/go/strings/strings.go +++ b/libgo/go/strings/strings.go @@ -275,20 +275,10 @@ func Join(a []string, sep string) string { } b := make([]byte, n) - bp := 0 - for i := 0; i < len(a); i++ { - s := a[i] - for j := 0; j < len(s); j++ { - b[bp] = s[j] - bp++ - } - if i+1 < len(a) { - s = sep - for j := 0; j < len(s); j++ { - b[bp] = s[j] - bp++ - } - } + bp := copy(b, a[0]) + for _, s := range a[1:] { + bp += copy(b[bp:], sep) + bp += copy(b[bp:], s) } 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. maxbytes := len(s) // length of b nbytes := 0 // number of bytes encoded in b - b := make([]byte, maxbytes) - for _, c := range s { + // The output buffer b is initialized on demand, the first + // time a character differs. + var b []byte + + for i, c := range s { rune := mapping(c) + if b == nil { + if rune == c { + continue + } + b = make([]byte, maxbytes) + nbytes = copy(b, s[:i]) + } if rune >= 0 { wid := 1 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) } } + if b == nil { + return s + } return string(b[0:nbytes]) } diff --git a/libgo/go/strings/strings_test.go b/libgo/go/strings/strings_test.go index 41e398782e6..c45b1485d8f 100644 --- a/libgo/go/strings/strings_test.go +++ b/libgo/go/strings/strings_test.go @@ -6,10 +6,12 @@ package strings_test import ( "os" + "reflect" "strconv" . "strings" "testing" "unicode" + "unsafe" "utf8" ) @@ -429,12 +431,32 @@ func TestMap(t *testing.T) { if m != expect { 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 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) { lower := "abcçdefgÄhıijklmnoö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) { // 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 { a[i] = i } @@ -627,10 +653,10 @@ func TestCaseConsistency(t *testing.T) { lower := ToLower(s) // 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) } - if n := utf8.RuneCountInString(lower); n != unicode.MaxRune+1 { + if n := utf8.RuneCountInString(lower); n != numRunes { t.Error("rune count wrong in lower:", n) } if !equal("ToUpper(upper)", ToUpper(upper), upper, t) { diff --git a/libgo/go/sync/atomic/atomic_test.go b/libgo/go/sync/atomic/atomic_test.go index 7b204b1d9f0..119ad0036fd 100644 --- a/libgo/go/sync/atomic/atomic_test.go +++ b/libgo/go/sync/atomic/atomic_test.go @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package atomic +package atomic_test import ( "runtime" + . "sync/atomic" "testing" "unsafe" ) @@ -27,6 +28,16 @@ const ( magic64 = 0xdeddeadbeefbeef ) +// Do the 64-bit functions panic? If so, don't bother testing. +var test64err = func() (err interface{}) { + defer func() { + err = recover() + }() + var x int64 + AddInt64(&x, 1) + return nil +}() + func TestAddInt32(t *testing.T) { var x struct { before int32 @@ -70,6 +81,10 @@ func TestAddUint32(t *testing.T) { } func TestAddInt64(t *testing.T) { + if test64err != nil { + t.Logf("Skipping 64-bit tests: %v", test64err) + return + } var x struct { before int64 i int64 @@ -91,6 +106,10 @@ func TestAddInt64(t *testing.T) { } func TestAddUint64(t *testing.T) { + if test64err != nil { + t.Logf("Skipping 64-bit tests: %v", test64err) + return + } var x struct { before uint64 i uint64 @@ -193,6 +212,10 @@ func TestCompareAndSwapUint32(t *testing.T) { } func TestCompareAndSwapInt64(t *testing.T) { + if test64err != nil { + t.Logf("Skipping 64-bit tests: %v", test64err) + return + } var x struct { before int64 i int64 @@ -222,6 +245,10 @@ func TestCompareAndSwapInt64(t *testing.T) { } func TestCompareAndSwapUint64(t *testing.T) { + if test64err != nil { + t.Logf("Skipping 64-bit tests: %v", test64err) + return + } var x struct { before uint64 i uint64 @@ -370,10 +397,11 @@ func hammerCompareAndSwapUintptr32(uval *uint32, count int) { } func TestHammer32(t *testing.T) { - const ( - n = 100000 - p = 4 - ) + const p = 4 + n := 100000 + if testing.Short() { + n = 1000 + } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(p)) for _, tt := range hammer32 { @@ -391,7 +419,7 @@ func TestHammer32(t *testing.T) { for i := 0; i < p; i++ { <-c } - if val != n*p { + if val != uint32(n)*p { t.Errorf("%s: val=%d want %d", tt.name, val, n*p) } } @@ -478,10 +506,15 @@ func hammerCompareAndSwapUintptr64(uval *uint64, count int) { } func TestHammer64(t *testing.T) { - const ( - n = 100000 - p = 4 - ) + if test64err != nil { + t.Logf("Skipping 64-bit tests: %v", test64err) + return + } + const p = 4 + n := 100000 + if testing.Short() { + n = 1000 + } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(p)) for _, tt := range hammer64 { @@ -499,7 +532,7 @@ func TestHammer64(t *testing.T) { for i := 0; i < p; i++ { <-c } - if val != n*p { + if val != uint64(n)*p { t.Errorf("%s: val=%d want %d", tt.name, val, n*p) } } diff --git a/libgo/go/sync/atomic/doc.go b/libgo/go/sync/atomic/doc.go index 1335def59f6..ec5a0d33af1 100644 --- a/libgo/go/sync/atomic/doc.go +++ b/libgo/go/sync/atomic/doc.go @@ -55,3 +55,8 @@ func AddUint64(val *uint64, delta uint64) (new uint64) // AddUintptr atomically adds delta to *val and returns the new value. func AddUintptr(val *uintptr, delta uintptr) (new uintptr) + +// Helper for ARM. Linker will discard on other systems +func panic64() { + panic("sync/atomic: broken 64-bit atomic operations (buggy QEMU)") +} diff --git a/libgo/go/sync/rwmutex_test.go b/libgo/go/sync/rwmutex_test.go index 405079270dc..9fb89f8e8a3 100644 --- a/libgo/go/sync/rwmutex_test.go +++ b/libgo/go/sync/rwmutex_test.go @@ -102,16 +102,20 @@ func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) { } func TestRWMutex(t *testing.T) { - HammerRWMutex(1, 1, 1000) - HammerRWMutex(1, 3, 1000) - HammerRWMutex(1, 10, 1000) - HammerRWMutex(4, 1, 1000) - HammerRWMutex(4, 3, 1000) - HammerRWMutex(4, 10, 1000) - HammerRWMutex(10, 1, 1000) - HammerRWMutex(10, 3, 1000) - HammerRWMutex(10, 10, 1000) - HammerRWMutex(10, 5, 10000) + n := 1000 + if testing.Short() { + n = 5 + } + HammerRWMutex(1, 1, n) + HammerRWMutex(1, 3, n) + HammerRWMutex(1, 10, n) + HammerRWMutex(4, 1, n) + HammerRWMutex(4, 3, n) + HammerRWMutex(4, 10, n) + HammerRWMutex(10, 1, n) + HammerRWMutex(10, 3, n) + HammerRWMutex(10, 10, n) + HammerRWMutex(10, 5, n) } func TestRLocker(t *testing.T) { diff --git a/libgo/go/syslog/syslog.go b/libgo/go/syslog/syslog.go index 711d5ddc74e..4ada113f1d7 100644 --- a/libgo/go/syslog/syslog.go +++ b/libgo/go/syslog/syslog.go @@ -67,7 +67,7 @@ func Dial(network, raddr string, priority Priority, prefix string) (w *Writer, e conn, err = unixSyslog() } else { var c net.Conn - c, err = net.Dial(network, "", raddr) + c, err = net.Dial(network, raddr) conn = netConn{c} } return &Writer{priority, prefix, conn}, err diff --git a/libgo/go/syslog/syslog_unix.go b/libgo/go/syslog/syslog_unix.go index b4daf88ee28..fa15e882d07 100644 --- a/libgo/go/syslog/syslog_unix.go +++ b/libgo/go/syslog/syslog_unix.go @@ -19,7 +19,7 @@ func unixSyslog() (conn serverConn, err os.Error) { for _, network := range logTypes { for _, path := range logPaths { raddr = path - conn, err := net.Dial(network, "", raddr) + conn, err := net.Dial(network, raddr) if err != nil { continue } else { diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go index ab8cf999a25..d1893907a56 100644 --- a/libgo/go/testing/testing.go +++ b/libgo/go/testing/testing.go @@ -48,6 +48,13 @@ import ( ) var ( + // The short flag requests that tests run more quickly, but its functionality + // is provided by test writers themselves. The testing package is just its + // home. The all.bash installation script sets it to make installation more + // efficient, but by default the flag is off so a plain "gotest" will do a + // full test of the package. + short = flag.Bool("test.short", false, "run smaller test suite to save time") + // Report as tests are run; default is silent for success. chatty = flag.Bool("test.v", false, "verbose: print additional output") match = flag.String("test.run", "", "regular expression to select tests to run") @@ -56,6 +63,11 @@ var ( cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution") ) +// Short reports whether the -test.short flag is set. +func Short() bool { + return *short +} + // Insert final newline if needed and tabs after internal newlines. func tabify(s string) string { @@ -174,7 +186,7 @@ func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern go tRunner(t, &tests[i]) <-t.ch ns += time.Nanoseconds() - tstr := fmt.Sprintf("(%.1f seconds)", float64(ns)/1e9) + tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9) if t.failed { println("--- FAIL:", tests[i].Name, tstr) print(t.errors) diff --git a/libgo/go/time/sleep_test.go b/libgo/go/time/sleep_test.go index 5fe4d7f15b5..eb6bb25fd4d 100644 --- a/libgo/go/time/sleep_test.go +++ b/libgo/go/time/sleep_test.go @@ -5,6 +5,7 @@ package time_test import ( + "fmt" "os" "syscall" "testing" @@ -132,8 +133,22 @@ func TestAfterStop(t *testing.T) { } } +func TestAfterQueuing(t *testing.T) { + // This test flakes out on some systems, + // so we'll try it a few times before declaring it a failure. + const attempts = 3 + err := os.NewError("!=nil") + for i := 0; i < attempts && err != nil; i++ { + if err = testAfterQueuing(t); err != nil { + t.Logf("attempt %v failed: %v", i, err) + } + } + if err != nil { + t.Fatal(err) + } +} + // For gccgo omit 0 for now because it can take too long to start the -// thread. var slots = []int{5, 3, 6, 6, 6, 1, 1, 2, 7, 9, 4, 8, /*0*/} type afterResult struct { @@ -145,7 +160,7 @@ func await(slot int, result chan<- afterResult, ac <-chan int64) { result <- afterResult{slot, <-ac} } -func TestAfterQueuing(t *testing.T) { +func testAfterQueuing(t *testing.T) os.Error { const ( Delta = 100 * 1e6 ) @@ -162,13 +177,14 @@ func TestAfterQueuing(t *testing.T) { for _, slot := range slots { r := <-result if r.slot != slot { - t.Fatalf("after queue got slot %d, expected %d", r.slot, slot) + return fmt.Errorf("after queue got slot %d, expected %d", r.slot, slot) } ns := r.t - t0 target := int64(slot * Delta) slop := int64(Delta) / 4 if ns < target-slop || ns > target+slop { - t.Fatalf("after queue slot %d arrived at %g, expected [%g,%g]", slot, float64(ns), float64(target-slop), float64(target+slop)) + return fmt.Errorf("after queue slot %d arrived at %g, expected [%g,%g]", slot, float64(ns), float64(target-slop), float64(target+slop)) } } + return nil } diff --git a/libgo/go/utf8/string_test.go b/libgo/go/utf8/string_test.go index 9dd84724730..f376b628c73 100644 --- a/libgo/go/utf8/string_test.go +++ b/libgo/go/utf8/string_test.go @@ -45,7 +45,12 @@ func TestScanBackwards(t *testing.T) { } } -const randCount = 100000 +func randCount() int { + if testing.Short() { + return 100 + } + return 100000 +} func TestRandomAccess(t *testing.T) { for _, s := range testStrings { @@ -58,7 +63,7 @@ func TestRandomAccess(t *testing.T) { t.Errorf("%s: expected %d runes; got %d", s, len(runes), str.RuneCount()) break } - for j := 0; j < randCount; j++ { + for j := 0; j < randCount(); j++ { i := rand.Intn(len(runes)) expect := runes[i] got := str.At(i) @@ -80,7 +85,7 @@ func TestRandomSliceAccess(t *testing.T) { t.Errorf("%s: expected %d runes; got %d", s, len(runes), str.RuneCount()) break } - for k := 0; k < randCount; k++ { + for k := 0; k < randCount(); k++ { i := rand.Intn(len(runes)) j := rand.Intn(len(runes) + 1) if i > j { // include empty strings diff --git a/libgo/go/websocket/client.go b/libgo/go/websocket/client.go index d8a7aa0a266..78c8b7f57bf 100644 --- a/libgo/go/websocket/client.go +++ b/libgo/go/websocket/client.go @@ -108,10 +108,10 @@ func Dial(url, protocol, origin string) (ws *Conn, err os.Error) { switch parsedUrl.Scheme { case "ws": - client, err = net.Dial("tcp", "", parsedUrl.Host) + client, err = net.Dial("tcp", parsedUrl.Host) case "wss": - client, err = tls.Dial("tcp", "", parsedUrl.Host, nil) + client, err = tls.Dial("tcp", parsedUrl.Host, nil) default: err = ErrBadScheme diff --git a/libgo/go/websocket/websocket_test.go b/libgo/go/websocket/websocket_test.go index 14d708a3bab..8b3cf8925a9 100644 --- a/libgo/go/websocket/websocket_test.go +++ b/libgo/go/websocket/websocket_test.go @@ -53,7 +53,7 @@ func TestEcho(t *testing.T) { once.Do(startServer) // websocket.Dial() - client, err := net.Dial("tcp", "", serverAddr) + client, err := net.Dial("tcp", serverAddr) if err != nil { t.Fatal("dialing", err) } @@ -84,7 +84,7 @@ func TestEchoDraft75(t *testing.T) { once.Do(startServer) // websocket.Dial() - client, err := net.Dial("tcp", "", serverAddr) + client, err := net.Dial("tcp", serverAddr) if err != nil { t.Fatal("dialing", err) } @@ -114,7 +114,7 @@ func TestEchoDraft75(t *testing.T) { func TestWithQuery(t *testing.T) { once.Do(startServer) - client, err := net.Dial("tcp", "", serverAddr) + client, err := net.Dial("tcp", serverAddr) if err != nil { t.Fatal("dialing", err) } @@ -131,7 +131,7 @@ func TestWithQuery(t *testing.T) { func TestWithProtocol(t *testing.T) { once.Do(startServer) - client, err := net.Dial("tcp", "", serverAddr) + client, err := net.Dial("tcp", serverAddr) if err != nil { t.Fatal("dialing", err) } @@ -200,7 +200,7 @@ func TestSmallBuffer(t *testing.T) { once.Do(startServer) // websocket.Dial() - client, err := net.Dial("tcp", "", serverAddr) + client, err := net.Dial("tcp", serverAddr) if err != nil { t.Fatal("dialing", err) } diff --git a/libgo/syscalls/socket.go b/libgo/syscalls/socket.go index e786653705b..28581a523e0 100644 --- a/libgo/syscalls/socket.go +++ b/libgo/syscalls/socket.go @@ -208,6 +208,13 @@ func Listen(fd int, n int) (errno int) { return; } +func GetsockoptInt(fd, level, opt int) (value, errno int) { + var n int32 + vallen := Socklen_t(4) + errno = libc_getsockopt(fd, level, opt, (*byte)(unsafe.Pointer(&n)), &vallen) + return int(n), errno +} + func setsockopt(fd, level, opt int, valueptr uintptr, length Socklen_t) (errno int) { r := libc_setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(valueptr)), length); @@ -383,5 +390,3 @@ func Shutdown(fd int, how int) (errno int) { if r < 0 { errno = GetErrno() } return; } - -// FIXME: No getsockopt. diff --git a/libgo/syscalls/sysfile_posix.go b/libgo/syscalls/sysfile_posix.go index 8b724983c63..46ac0e07e1d 100644 --- a/libgo/syscalls/sysfile_posix.go +++ b/libgo/syscalls/sysfile_posix.go @@ -209,7 +209,7 @@ func FDZero(set *FdSet_t) { func Select(nfds int, r *FdSet_t, w *FdSet_t, e *FdSet_t, timeout *Timeval) (n int, errno int) { n = libc_select(nfds, (*byte)(unsafe.Pointer(r)), - (*byte)(unsafe.Pointer(e)), + (*byte)(unsafe.Pointer(w)), (*byte)(unsafe.Pointer(e)), timeout); if n < 0 { errno = GetErrno() } return; diff --git a/libgo/testsuite/gotest b/libgo/testsuite/gotest index 20ae0a0bd25..bcd725136b9 100755 --- a/libgo/testsuite/gotest +++ b/libgo/testsuite/gotest @@ -357,7 +357,7 @@ case "x$dejagnu" in xno) ${GC} -g -c _testmain.go ${GL} *.o ${GOLIBS} - ./a.out "$@" + ./a.out -test.short "$@" ;; xyes) rm -rf ../testsuite/*.o diff --git a/libgo/testsuite/libgo.testmain/testmain.exp b/libgo/testsuite/libgo.testmain/testmain.exp index efdd28d9162..f201de7f76a 100644 --- a/libgo/testsuite/libgo.testmain/testmain.exp +++ b/libgo/testsuite/libgo.testmain/testmain.exp @@ -54,7 +54,7 @@ if ![ string match "" $comp_output ] { exit 1 } -set result [libgo_load "./a.exe" "" ""] +set result [libgo_load "./a.exe" "-test.short" ""] set status [lindex $result 0] $status go