Commit 8efc020d authored by Robert Griesemer's avatar Robert Griesemer

go/printer: factor some frequently used code

Added a cache to compensate for extra call overhead.
go test -bench=Print marginally faster (in the noise).

R=r
CC=golang-dev
https://golang.org/cl/5574061
parent d665ea98
...@@ -72,7 +72,7 @@ func (p *printer) setComment(g *ast.CommentGroup) { ...@@ -72,7 +72,7 @@ func (p *printer) setComment(g *ast.CommentGroup) {
// for some reason there are pending comments; this // for some reason there are pending comments; this
// should never happen - handle gracefully and flush // should never happen - handle gracefully and flush
// all comments up to g, ignore anything after that // all comments up to g, ignore anything after that
p.flush(p.fset.Position(g.List[0].Pos()), token.ILLEGAL) p.flush(p.posFor(g.List[0].Pos()), token.ILLEGAL)
} }
p.comments[0] = g p.comments[0] = g
p.cindex = 0 p.cindex = 0
...@@ -122,10 +122,10 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp ...@@ -122,10 +122,10 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
p.print(blank) p.print(blank)
} }
prev := p.fset.Position(prev0) prev := p.posFor(prev0)
next := p.fset.Position(next0) next := p.posFor(next0)
line := p.fset.Position(list[0].Pos()).Line line := p.lineFor(list[0].Pos())
endLine := p.fset.Position(list[len(list)-1].End()).Line endLine := p.lineFor(list[len(list)-1].End())
if prev.IsValid() && prev.Line == line && line == endLine { if prev.IsValid() && prev.Line == line && line == endLine {
// all list entries on a single line // all list entries on a single line
...@@ -169,7 +169,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp ...@@ -169,7 +169,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
// print all list elements // print all list elements
for i, x := range list { for i, x := range list {
prevLine := line prevLine := line
line = p.fset.Position(x.Pos()).Line line = p.lineFor(x.Pos())
// determine if the next linebreak, if any, needs to use formfeed: // determine if the next linebreak, if any, needs to use formfeed:
// in general, use the entire node size to make the decision; for // in general, use the entire node size to make the decision; for
...@@ -272,16 +272,16 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp ...@@ -272,16 +272,16 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) { func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) {
p.print(fields.Opening, token.LPAREN) p.print(fields.Opening, token.LPAREN)
if len(fields.List) > 0 { if len(fields.List) > 0 {
prevLine := p.fset.Position(fields.Opening).Line prevLine := p.lineFor(fields.Opening)
ws := indent ws := indent
for i, par := range fields.List { for i, par := range fields.List {
// determine par begin and end line (may be different // determine par begin and end line (may be different
// if there are multiple parameter names for this par // if there are multiple parameter names for this par
// or the type is on a separate line) // or the type is on a separate line)
var parLineBeg int var parLineBeg int
var parLineEnd = p.fset.Position(par.Type.Pos()).Line var parLineEnd = p.lineFor(par.Type.Pos())
if len(par.Names) > 0 { if len(par.Names) > 0 {
parLineBeg = p.fset.Position(par.Names[0].Pos()).Line parLineBeg = p.lineFor(par.Names[0].Pos())
} else { } else {
parLineBeg = parLineEnd parLineBeg = parLineEnd
} }
...@@ -314,7 +314,7 @@ func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) { ...@@ -314,7 +314,7 @@ func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) {
} }
// if the closing ")" is on a separate line from the last parameter, // if the closing ")" is on a separate line from the last parameter,
// print an additional "," and line break // print an additional "," and line break
if closing := p.fset.Position(fields.Closing).Line; 0 < prevLine && prevLine < closing { if closing := p.lineFor(fields.Closing); 0 < prevLine && prevLine < closing {
p.print(",") p.print(",")
p.linebreak(closing, 0, ignore, true) p.linebreak(closing, 0, ignore, true)
} }
...@@ -380,8 +380,8 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) ...@@ -380,8 +380,8 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
lbrace := fields.Opening lbrace := fields.Opening
list := fields.List list := fields.List
rbrace := fields.Closing rbrace := fields.Closing
hasComments := isIncomplete || p.commentBefore(p.fset.Position(rbrace)) hasComments := isIncomplete || p.commentBefore(p.posFor(rbrace))
srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.fset.Position(lbrace).Line == p.fset.Position(rbrace).Line srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.lineFor(lbrace) == p.lineFor(rbrace)
if !hasComments && srcIsOneLine { if !hasComments && srcIsOneLine {
// possibly a one-line struct/interface // possibly a one-line struct/interface
...@@ -424,7 +424,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) ...@@ -424,7 +424,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
var ml bool var ml bool
for i, f := range list { for i, f := range list {
if i > 0 { if i > 0 {
p.linebreak(p.fset.Position(f.Pos()).Line, 1, ignore, ml) p.linebreak(p.lineFor(f.Pos()), 1, ignore, ml)
} }
ml = false ml = false
extraTabs := 0 extraTabs := 0
...@@ -459,7 +459,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) ...@@ -459,7 +459,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
if len(list) > 0 { if len(list) > 0 {
p.print(formfeed) p.print(formfeed)
} }
p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't lose the last line comment p.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment
p.setLineComment("// contains filtered or unexported fields") p.setLineComment("// contains filtered or unexported fields")
} }
...@@ -468,7 +468,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) ...@@ -468,7 +468,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
var ml bool var ml bool
for i, f := range list { for i, f := range list {
if i > 0 { if i > 0 {
p.linebreak(p.fset.Position(f.Pos()).Line, 1, ignore, ml) p.linebreak(p.lineFor(f.Pos()), 1, ignore, ml)
} }
ml = false ml = false
p.setComment(f.Doc) p.setComment(f.Doc)
...@@ -486,7 +486,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) ...@@ -486,7 +486,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
if len(list) > 0 { if len(list) > 0 {
p.print(formfeed) p.print(formfeed)
} }
p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't lose the last line comment p.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment
p.setLineComment("// contains filtered or unexported methods") p.setLineComment("// contains filtered or unexported methods")
} }
...@@ -642,7 +642,7 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiL ...@@ -642,7 +642,7 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiL
p.print(blank) p.print(blank)
} }
xline := p.pos.Line // before the operator (it may be on the next line!) xline := p.pos.Line // before the operator (it may be on the next line!)
yline := p.fset.Position(x.Y.Pos()).Line yline := p.lineFor(x.Y.Pos())
p.print(x.OpPos, x.Op) p.print(x.OpPos, x.Op)
if xline != yline && xline > 0 && yline > 0 { if xline != yline && xline > 0 && yline > 0 {
// at least one line break, but respect an extra empty line // at least one line break, but respect an extra empty line
...@@ -935,7 +935,7 @@ func (p *printer) stmtList(list []ast.Stmt, _indent int, nextIsRBrace bool) { ...@@ -935,7 +935,7 @@ func (p *printer) stmtList(list []ast.Stmt, _indent int, nextIsRBrace bool) {
for i, s := range list { for i, s := range list {
// _indent == 0 only for lists of switch/select case clauses; // _indent == 0 only for lists of switch/select case clauses;
// in those cases each clause is a new section // in those cases each clause is a new section
p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, i == 0 || _indent == 0 || multiLine) p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || _indent == 0 || multiLine)
multiLine = false multiLine = false
p.stmt(s, nextIsRBrace && i == len(list)-1, &multiLine) p.stmt(s, nextIsRBrace && i == len(list)-1, &multiLine)
} }
...@@ -948,7 +948,7 @@ func (p *printer) stmtList(list []ast.Stmt, _indent int, nextIsRBrace bool) { ...@@ -948,7 +948,7 @@ func (p *printer) stmtList(list []ast.Stmt, _indent int, nextIsRBrace bool) {
func (p *printer) block(s *ast.BlockStmt, indent int) { func (p *printer) block(s *ast.BlockStmt, indent int) {
p.print(s.Pos(), token.LBRACE) p.print(s.Pos(), token.LBRACE)
p.stmtList(s.List, indent, true) p.stmtList(s.List, indent, true)
p.linebreak(p.fset.Position(s.Rbrace).Line, 1, ignore, true) p.linebreak(p.lineFor(s.Rbrace), 1, ignore, true)
p.print(s.Rbrace, token.RBRACE) p.print(s.Rbrace, token.RBRACE)
} }
...@@ -1049,7 +1049,7 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) { ...@@ -1049,7 +1049,7 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
break break
} }
} else { } else {
p.linebreak(p.fset.Position(s.Stmt.Pos()).Line, 1, ignore, true) p.linebreak(p.lineFor(s.Stmt.Pos()), 1, ignore, true)
} }
p.stmt(s.Stmt, nextIsRBrace, multiLine) p.stmt(s.Stmt, nextIsRBrace, multiLine)
...@@ -1161,7 +1161,7 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) { ...@@ -1161,7 +1161,7 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
case *ast.SelectStmt: case *ast.SelectStmt:
p.print(token.SELECT, blank) p.print(token.SELECT, blank)
body := s.Body body := s.Body
if len(body.List) == 0 && !p.commentBefore(p.fset.Position(body.Rbrace)) { if len(body.List) == 0 && !p.commentBefore(p.posFor(body.Rbrace)) {
// print empty select statement w/o comments on one line // print empty select statement w/o comments on one line
p.print(body.Lbrace, token.LBRACE, body.Rbrace, token.RBRACE) p.print(body.Lbrace, token.LBRACE, body.Rbrace, token.RBRACE)
} else { } else {
...@@ -1353,7 +1353,7 @@ func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) { ...@@ -1353,7 +1353,7 @@ func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) {
var ml bool var ml bool
for i, s := range d.Specs { for i, s := range d.Specs {
if i > 0 { if i > 0 {
p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, ml) p.linebreak(p.lineFor(s.Pos()), 1, ignore, ml)
} }
ml = false ml = false
p.valueSpec(s.(*ast.ValueSpec), keepType[i], false, &ml) p.valueSpec(s.(*ast.ValueSpec), keepType[i], false, &ml)
...@@ -1362,7 +1362,7 @@ func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) { ...@@ -1362,7 +1362,7 @@ func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) {
var ml bool var ml bool
for i, s := range d.Specs { for i, s := range d.Specs {
if i > 0 { if i > 0 {
p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, ml) p.linebreak(p.lineFor(s.Pos()), 1, ignore, ml)
} }
ml = false ml = false
p.spec(s, n, false, &ml) p.spec(s, n, false, &ml)
...@@ -1419,11 +1419,11 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) { ...@@ -1419,11 +1419,11 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool { func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
pos1 := b.Pos() pos1 := b.Pos()
pos2 := b.Rbrace pos2 := b.Rbrace
if pos1.IsValid() && pos2.IsValid() && p.fset.Position(pos1).Line != p.fset.Position(pos2).Line { if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) {
// opening and closing brace are on different lines - don't make it a one-liner // opening and closing brace are on different lines - don't make it a one-liner
return false return false
} }
if len(b.List) > 5 || p.commentBefore(p.fset.Position(pos2)) { if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) {
// too many statements or there is a comment inside - don't make it a one-liner // too many statements or there is a comment inside - don't make it a one-liner
return false return false
} }
...@@ -1474,7 +1474,7 @@ func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool, multiLi ...@@ -1474,7 +1474,7 @@ func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool, multiLi
// are on the same line; if they are on different lines (or unknown) // are on the same line; if they are on different lines (or unknown)
// the result is infinity. // the result is infinity.
func (p *printer) distance(from0 token.Pos, to token.Position) int { func (p *printer) distance(from0 token.Pos, to token.Position) int {
from := p.fset.Position(from0) from := p.posFor(from0)
if from.IsValid() && to.IsValid() && from.Line == to.Line { if from.IsValid() && to.IsValid() && from.Line == to.Line {
return to.Column - from.Column return to.Column - from.Column
} }
...@@ -1543,7 +1543,7 @@ func (p *printer) file(src *ast.File) { ...@@ -1543,7 +1543,7 @@ func (p *printer) file(src *ast.File) {
if prev != tok || getDoc(d) != nil { if prev != tok || getDoc(d) != nil {
min = 2 min = 2
} }
p.linebreak(p.fset.Position(d.Pos()).Line, min, ignore, false) p.linebreak(p.lineFor(d.Pos()), min, ignore, false)
p.decl(d, ignoreMultiLine) p.decl(d, ignoreMultiLine)
} }
} }
......
...@@ -75,6 +75,10 @@ type printer struct { ...@@ -75,6 +75,10 @@ type printer struct {
// Cache of already computed node sizes. // Cache of already computed node sizes.
nodeSizes map[ast.Node]int nodeSizes map[ast.Node]int
// Cache of most recently computed line position.
cachedPos token.Pos
cachedLine int // line corresponding to cachedPos
} }
func (p *printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) { func (p *printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) {
...@@ -82,6 +86,7 @@ func (p *printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node] ...@@ -82,6 +86,7 @@ func (p *printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]
p.fset = fset p.fset = fset
p.wsbuf = make([]whiteSpace, 0, 16) // whitespace sequences are short p.wsbuf = make([]whiteSpace, 0, 16) // whitespace sequences are short
p.nodeSizes = nodeSizes p.nodeSizes = nodeSizes
p.cachedPos = -1
} }
func (p *printer) internalError(msg ...interface{}) { func (p *printer) internalError(msg ...interface{}) {
...@@ -92,6 +97,19 @@ func (p *printer) internalError(msg ...interface{}) { ...@@ -92,6 +97,19 @@ func (p *printer) internalError(msg ...interface{}) {
} }
} }
func (p *printer) posFor(pos token.Pos) token.Position {
// not used frequently enough to cache entire token.Position
return p.fset.Position(pos)
}
func (p *printer) lineFor(pos token.Pos) int {
if pos != p.cachedPos {
p.cachedPos = pos
p.cachedLine = p.fset.Position(pos).Line
}
return p.cachedLine
}
// writeByte writes ch to p.output and updates p.pos. // writeByte writes ch to p.output and updates p.pos.
func (p *printer) writeByte(ch byte) { func (p *printer) writeByte(ch byte) {
p.output.WriteByte(ch) p.output.WriteByte(ch)
...@@ -529,7 +547,7 @@ func (p *printer) writeComment(comment *ast.Comment) { ...@@ -529,7 +547,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
// shortcut common case of //-style comments // shortcut common case of //-style comments
if text[1] == '/' { if text[1] == '/' {
p.writeItem(p.fset.Position(comment.Pos()), text, true) p.writeItem(p.posFor(comment.Pos()), text, true)
return return
} }
...@@ -540,7 +558,7 @@ func (p *printer) writeComment(comment *ast.Comment) { ...@@ -540,7 +558,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
// write comment lines, separated by formfeed, // write comment lines, separated by formfeed,
// without a line break after the last line // without a line break after the last line
pos := p.fset.Position(comment.Pos()) pos := p.posFor(comment.Pos())
for i, line := range lines { for i, line := range lines {
if i > 0 { if i > 0 {
p.writeByte('\f') p.writeByte('\f')
...@@ -602,14 +620,14 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro ...@@ -602,14 +620,14 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro
var last *ast.Comment var last *ast.Comment
for ; p.commentBefore(next); p.cindex++ { for ; p.commentBefore(next); p.cindex++ {
for _, c := range p.comments[p.cindex].List { for _, c := range p.comments[p.cindex].List {
p.writeCommentPrefix(p.fset.Position(c.Pos()), next, last, c, tok.IsKeyword()) p.writeCommentPrefix(p.posFor(c.Pos()), next, last, c, tok.IsKeyword())
p.writeComment(c) p.writeComment(c)
last = c last = c
} }
} }
if last != nil { if last != nil {
if last.Text[1] == '*' && p.fset.Position(last.Pos()).Line == next.Line { if last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line {
// the last comment is a /*-style comment and the next item // the last comment is a /*-style comment and the next item
// follows on the same line: separate with an extra blank // follows on the same line: separate with an extra blank
p.writeByte(' ') p.writeByte(' ')
...@@ -770,7 +788,7 @@ func (p *printer) print(args ...interface{}) { ...@@ -770,7 +788,7 @@ func (p *printer) print(args ...interface{}) {
tok = x tok = x
case token.Pos: case token.Pos:
if x.IsValid() { if x.IsValid() {
next = p.fset.Position(x) // accurate position of next item next = p.posFor(x) // accurate position of next item
} }
tok = p.lastTok tok = p.lastTok
case string: case string:
...@@ -813,7 +831,7 @@ func (p *printer) print(args ...interface{}) { ...@@ -813,7 +831,7 @@ func (p *printer) print(args ...interface{}) {
// before the next position in the source code. // before the next position in the source code.
// //
func (p *printer) commentBefore(next token.Position) bool { func (p *printer) commentBefore(next token.Position) bool {
return p.cindex < len(p.comments) && p.fset.Position(p.comments[p.cindex].List[0].Pos()).Offset < next.Offset return p.cindex < len(p.comments) && p.posFor(p.comments[p.cindex].List[0].Pos()).Offset < next.Offset
} }
// Flush prints any pending comments and whitespace occurring textually // Flush prints any pending comments and whitespace occurring textually
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment