rsc.io/go@v0.0.0-20150416155037-e040fd465409/src/cmd/internal/obj/obj.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package obj 6 7 import ( 8 "fmt" 9 "path/filepath" 10 "sort" 11 "strings" 12 ) 13 14 // A LineHist records the history of the file input stack, which maps the virtual line number, 15 // an incrementing count of lines processed in any input file and typically named lineno, 16 // to a stack of file:line pairs showing the path of inclusions that led to that position. 17 // The first line directive (//line in Go, #line in assembly) is treated as pushing 18 // a new entry on the stack, so that errors can report both the actual and translated 19 // line number. 20 // 21 // In typical use, the virtual lineno begins at 1, and file line numbers also begin at 1, 22 // but the only requirements placed upon the numbers by this code are: 23 // - calls to Push, Update, and Pop must be monotonically increasing in lineno 24 // - except as specified by those methods, virtual and file line number increase 25 // together, so that given (only) calls Push(10, "x.go", 1) and Pop(15), 26 // virtual line 12 corresponds to x.go line 3. 27 type LineHist struct { 28 Top *LineStack // current top of stack 29 Ranges []LineRange // ranges for lookup 30 Dir string // directory to qualify relative paths 31 TrimPathPrefix string // remove leading TrimPath from recorded file names 32 GOROOT string // current GOROOT 33 GOROOT_FINAL string // target GOROOT 34 } 35 36 // A LineStack is an entry in the recorded line history. 37 // Although the history at any given line number is a stack, 38 // the record for all line processed forms a tree, with common 39 // stack prefixes acting as parents. 40 type LineStack struct { 41 Parent *LineStack // parent in inclusion stack 42 Lineno int // virtual line number where this entry takes effect 43 File string // file name used to open source file, for error messages 44 AbsFile string // absolute file name, for pcln tables 45 FileLine int // line number in file at Lineno 46 Directive bool 47 Sym *LSym // for linkgetline - TODO(rsc): remove 48 } 49 50 func (stk *LineStack) fileLineAt(lineno int) int { 51 return stk.FileLine + lineno - stk.Lineno 52 } 53 54 // The span of valid linenos in the recorded line history can be broken 55 // into a set of ranges, each with a particular stack. 56 // A LineRange records one such range. 57 type LineRange struct { 58 Start int // starting lineno 59 Stack *LineStack // top of stack for this range 60 } 61 62 // startRange starts a new range with the given top of stack. 63 func (h *LineHist) startRange(lineno int, top *LineStack) { 64 h.Top = top 65 h.Ranges = append(h.Ranges, LineRange{top.Lineno, top}) 66 } 67 68 // setFile sets stk.File = file and also derives stk.AbsFile. 69 func (h *LineHist) setFile(stk *LineStack, file string) { 70 // Note: The exclusion of stk.Directive may be wrong but matches what we've done before. 71 // The check for < avoids putting a path prefix on "<autogenerated>". 72 abs := file 73 if h.Dir != "" && !filepath.IsAbs(file) && !strings.HasPrefix(file, "<") && !stk.Directive { 74 abs = filepath.Join(h.Dir, file) 75 } 76 77 // Remove leading TrimPathPrefix, or else rewrite $GOROOT to $GOROOT_FINAL. 78 if h.TrimPathPrefix != "" && hasPathPrefix(abs, h.TrimPathPrefix) { 79 if abs == h.TrimPathPrefix { 80 abs = "" 81 } else { 82 abs = abs[len(h.TrimPathPrefix)+1:] 83 } 84 } else if h.GOROOT_FINAL != "" && h.GOROOT_FINAL != h.GOROOT && hasPathPrefix(abs, h.GOROOT) { 85 abs = h.GOROOT_FINAL + abs[len(h.GOROOT):] 86 } 87 if abs == "" { 88 abs = "??" 89 } 90 abs = filepath.Clean(abs) 91 stk.AbsFile = abs 92 93 if file == "" { 94 file = "??" 95 } 96 stk.File = file 97 } 98 99 // Does s have t as a path prefix? 100 // That is, does s == t or does s begin with t followed by a slash? 101 // For portability, we allow ASCII case folding, so that hasPathPrefix("a/b/c", "A/B") is true. 102 // Similarly, we allow slash folding, so that hasPathPrefix("a/b/c", "a\\b") is true. 103 // We do not allow full Unicode case folding, for fear of causing more confusion 104 // or harm than good. (For an example of the kinds of things that can go wrong, 105 // see http://article.gmane.org/gmane.linux.kernel/1853266.) 106 func hasPathPrefix(s string, t string) bool { 107 if len(t) > len(s) { 108 return false 109 } 110 var i int 111 for i = 0; i < len(t); i++ { 112 cs := int(s[i]) 113 ct := int(t[i]) 114 if 'A' <= cs && cs <= 'Z' { 115 cs += 'a' - 'A' 116 } 117 if 'A' <= ct && ct <= 'Z' { 118 ct += 'a' - 'A' 119 } 120 if cs == '\\' { 121 cs = '/' 122 } 123 if ct == '\\' { 124 ct = '/' 125 } 126 if cs != ct { 127 return false 128 } 129 } 130 return i >= len(s) || s[i] == '/' || s[i] == '\\' 131 } 132 133 // Push records that at that lineno a new file with the given name was pushed onto the input stack. 134 func (h *LineHist) Push(lineno int, file string) { 135 stk := &LineStack{ 136 Parent: h.Top, 137 Lineno: lineno, 138 FileLine: 1, 139 } 140 h.setFile(stk, file) 141 h.startRange(lineno, stk) 142 } 143 144 // Pop records that at lineno the current file was popped from the input stack. 145 func (h *LineHist) Pop(lineno int) { 146 top := h.Top 147 if top == nil { 148 return 149 } 150 if top.Directive && top.Parent != nil { // pop #line level too 151 top = top.Parent 152 } 153 next := top.Parent 154 if next == nil { 155 h.Top = nil 156 h.Ranges = append(h.Ranges, LineRange{lineno, nil}) 157 return 158 } 159 160 // Popping included file. Update parent offset to account for 161 // the virtual line number range taken by the included file. 162 // Cannot modify the LineStack directly, or else lookups 163 // for the earlier line numbers will get the wrong answers, 164 // so make a new one. 165 stk := new(LineStack) 166 *stk = *next 167 stk.Lineno = lineno 168 stk.FileLine = next.fileLineAt(top.Lineno) 169 h.startRange(lineno, stk) 170 } 171 172 // Update records that at lineno the file name and line number were changed using 173 // a line directive (//line in Go, #line in assembly). 174 func (h *LineHist) Update(lineno int, file string, line int) { 175 top := h.Top 176 if top == nil { 177 return // shouldn't happen 178 } 179 var stk *LineStack 180 if top.Directive { 181 // Update existing entry, except make copy to avoid changing earlier history. 182 stk = new(LineStack) 183 *stk = *top 184 } else { 185 // Push new entry. 186 stk = &LineStack{ 187 Parent: top, 188 Directive: true, 189 } 190 } 191 stk.Lineno = lineno 192 if stk.File != file { 193 h.setFile(stk, file) // only retain string if needed 194 } 195 stk.FileLine = line 196 h.startRange(lineno, stk) 197 } 198 199 // AddImport adds a package to the list of imported packages. 200 func (ctxt *Link) AddImport(pkg string) { 201 ctxt.Imports = append(ctxt.Imports, pkg) 202 } 203 204 // At returns the input stack in effect at lineno. 205 func (h *LineHist) At(lineno int) *LineStack { 206 i := sort.Search(len(h.Ranges), func(i int) bool { 207 return h.Ranges[i].Start > lineno 208 }) 209 // Found first entry beyond lineno. 210 if i == 0 { 211 return nil 212 } 213 return h.Ranges[i-1].Stack 214 } 215 216 // LineString returns a string giving the file and line number 217 // corresponding to lineno, for use in error messages. 218 func (h *LineHist) LineString(lineno int) string { 219 stk := h.At(lineno) 220 if stk == nil { 221 return "<unknown line number>" 222 } 223 224 text := fmt.Sprintf("%s:%d", stk.File, stk.fileLineAt(lineno)) 225 if stk.Directive && stk.Parent != nil { 226 stk = stk.Parent 227 text += fmt.Sprintf("[%s:%d]", stk.File, stk.fileLineAt(lineno)) 228 } 229 const showFullStack = false // was used by old C compilers 230 if showFullStack { 231 for stk.Parent != nil { 232 lineno = stk.Lineno - 1 233 stk = stk.Parent 234 text += fmt.Sprintf(" %s:%d", stk.File, stk.fileLineAt(lineno)) 235 if stk.Directive && stk.Parent != nil { 236 stk = stk.Parent 237 text += fmt.Sprintf("[%s:%d]", stk.File, stk.fileLineAt(lineno)) 238 } 239 } 240 } 241 return text 242 } 243 244 // TODO(rsc): Replace call sites with use of ctxt.LineHist. 245 // Note that all call sites use showAll=false, showFullPath=false. 246 func Linklinefmt(ctxt *Link, lineno int, showAll, showFullPath bool) string { 247 return ctxt.LineHist.LineString(lineno) 248 } 249 250 // FileLine returns the file name and line number 251 // at the top of the stack for the given lineno. 252 func (h *LineHist) FileLine(lineno int) (file string, line int) { 253 stk := h.At(lineno) 254 if stk == nil { 255 return "??", 0 256 } 257 return stk.File, stk.fileLineAt(lineno) 258 } 259 260 // AbsFileLine returns the absolute file name and line number 261 // at the top of the stack for the given lineno. 262 func (h *LineHist) AbsFileLine(lineno int) (file string, line int) { 263 stk := h.At(lineno) 264 if stk == nil { 265 return "??", 0 266 } 267 return stk.AbsFile, stk.fileLineAt(lineno) 268 } 269 270 // This is a simplified copy of linklinefmt above. 271 // It doesn't allow printing the full stack, and it returns the file name and line number separately. 272 // TODO: Unify with linklinefmt somehow. 273 func linkgetline(ctxt *Link, lineno int32, f **LSym, l *int32) { 274 stk := ctxt.LineHist.At(int(lineno)) 275 if stk == nil || stk.AbsFile == "" { 276 *f = Linklookup(ctxt, "??", HistVersion) 277 *l = 0 278 return 279 } 280 if stk.Sym == nil { 281 stk.Sym = Linklookup(ctxt, stk.AbsFile, HistVersion) 282 } 283 *f = stk.Sym 284 *l = int32(stk.fileLineAt(int(lineno))) 285 } 286 287 func Linkprfile(ctxt *Link, line int) { 288 fmt.Printf("%s ", ctxt.LineHist.LineString(line)) 289 } 290 291 // Linklinehist pushes, amends, or pops an entry on the line history stack. 292 // If f != "<pop>" and n == 0, the call pushes the start of a new file named f at lineno. 293 // If f != "<pop>" and n > 0, the call amends the top of the stack to record that lineno 294 // now corresponds to f at line n. 295 // If f == "<pop>", the call pops the topmost entry from the stack, picking up 296 // the parent file at the line following the one where the corresponding push occurred. 297 // 298 // If n < 0, linklinehist records f as a package required by the current compilation 299 // (nothing to do with line numbers). 300 // 301 // TODO(rsc): Replace uses with direct calls to ctxt.Hist methods. 302 func Linklinehist(ctxt *Link, lineno int, f string, n int) { 303 switch { 304 case n < 0: 305 ctxt.AddImport(f) 306 307 case f == "<pop>": 308 ctxt.LineHist.Pop(lineno) 309 310 case n == 0: 311 ctxt.LineHist.Push(lineno, f) 312 313 default: 314 ctxt.LineHist.Update(lineno, f, n) 315 } 316 }