1// Copyright 2016 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// This file implements the compressed encoding of source 6// positions using a lookup table. 7 8package src 9 10// XPos is a more compact representation of Pos. 11type XPos struct { 12 index int32 13 lico 14} 15 16// NoXPos is a valid unknown position. 17var NoXPos XPos 18 19// IsKnown reports whether the position p is known. 20// XPos.IsKnown() matches Pos.IsKnown() for corresponding 21// positions. 22func (p XPos) IsKnown() bool { 23 return p.index != 0 || p.Line() != 0 24} 25 26// Before reports whether the position p comes before q in the source. 27// For positions with different bases, ordering is by base index. 28func (p XPos) Before(q XPos) bool { 29 n, m := p.index, q.index 30 return n < m || n == m && p.lico < q.lico 31} 32 33// SameFile reports whether p and q are positions in the same file. 34func (p XPos) SameFile(q XPos) bool { 35 return p.index == q.index 36} 37 38// SameFileAndLine reports whether p and q are positions on the same line in the same file. 39func (p XPos) SameFileAndLine(q XPos) bool { 40 return p.index == q.index && p.lico.SameLine(q.lico) 41} 42 43// After reports whether the position p comes after q in the source. 44// For positions with different bases, ordering is by base index. 45func (p XPos) After(q XPos) bool { 46 n, m := p.index, q.index 47 return n > m || n == m && p.lico > q.lico 48} 49 50// WithNotStmt returns the same location to be marked with DWARF is_stmt=0 51func (p XPos) WithNotStmt() XPos { 52 p.lico = p.lico.withNotStmt() 53 return p 54} 55 56// WithDefaultStmt returns the same location with undetermined is_stmt 57func (p XPos) WithDefaultStmt() XPos { 58 p.lico = p.lico.withDefaultStmt() 59 return p 60} 61 62// WithIsStmt returns the same location to be marked with DWARF is_stmt=1 63func (p XPos) WithIsStmt() XPos { 64 p.lico = p.lico.withIsStmt() 65 return p 66} 67 68// WithBogusLine returns a bogus line that won't match any recorded for the source code. 69// Its use is to disrupt the statements within an infinite loop so that the debugger 70// will not itself loop infinitely waiting for the line number to change. 71// gdb chooses not to display the bogus line; delve shows it with a complaint, but the 72// alternative behavior is to hang. 73func (p XPos) WithBogusLine() XPos { 74 if p.index == 0 { 75 // See #35652 76 panic("Assigning a bogus line to XPos with no file will cause mysterious downstream failures.") 77 } 78 p.lico = makeBogusLico() 79 return p 80} 81 82// WithXlogue returns the same location but marked with DWARF function prologue/epilogue 83func (p XPos) WithXlogue(x PosXlogue) XPos { 84 p.lico = p.lico.withXlogue(x) 85 return p 86} 87 88// LineNumber returns a string for the line number, "?" if it is not known. 89func (p XPos) LineNumber() string { 90 if !p.IsKnown() { 91 return "?" 92 } 93 return p.lico.lineNumber() 94} 95 96// FileIndex returns a smallish non-negative integer corresponding to the 97// file for this source position. Smallish is relative; it can be thousands 98// large, but not millions. 99func (p XPos) FileIndex() int32 { 100 return p.index 101} 102 103func (p XPos) LineNumberHTML() string { 104 if !p.IsKnown() { 105 return "?" 106 } 107 return p.lico.lineNumberHTML() 108} 109 110// AtColumn1 returns the same location but shifted to column 1. 111func (p XPos) AtColumn1() XPos { 112 p.lico = p.lico.atColumn1() 113 return p 114} 115 116// A PosTable tracks Pos -> XPos conversions and vice versa. 117// Its zero value is a ready-to-use PosTable. 118type PosTable struct { 119 baseList []*PosBase 120 indexMap map[*PosBase]int 121 nameMap map[string]int // Maps file symbol name to index for debug information. 122} 123 124// XPos returns the corresponding XPos for the given pos, 125// adding pos to t if necessary. 126func (t *PosTable) XPos(pos Pos) XPos { 127 return XPos{t.baseIndex(pos.base), pos.lico} 128} 129 130func (t *PosTable) baseIndex(base *PosBase) int32 { 131 if base == nil { 132 return 0 133 } 134 135 if i, ok := t.indexMap[base]; ok { 136 return int32(i) 137 } 138 139 if base.fileIndex >= 0 { 140 panic("PosBase already registered with a PosTable") 141 } 142 143 if t.indexMap == nil { 144 t.baseList = append(t.baseList, nil) 145 t.indexMap = make(map[*PosBase]int) 146 t.nameMap = make(map[string]int) 147 } 148 149 i := len(t.baseList) 150 t.indexMap[base] = i 151 t.baseList = append(t.baseList, base) 152 153 fileIndex, ok := t.nameMap[base.absFilename] 154 if !ok { 155 fileIndex = len(t.nameMap) 156 t.nameMap[base.absFilename] = fileIndex 157 } 158 base.fileIndex = fileIndex 159 160 return int32(i) 161} 162 163// Pos returns the corresponding Pos for the given p. 164// If p cannot be translated via t, the function panics. 165func (t *PosTable) Pos(p XPos) Pos { 166 var base *PosBase 167 if p.index != 0 { 168 base = t.baseList[p.index] 169 } 170 return Pos{base, p.lico} 171} 172 173// FileTable returns a slice of all files used to build this package. 174func (t *PosTable) FileTable() []string { 175 // Create a LUT of the global package level file indices. This table is what 176 // is written in the debug_lines header, the file[N] will be referenced as 177 // N+1 in the debug_lines table. 178 fileLUT := make([]string, len(t.nameMap)) 179 for str, i := range t.nameMap { 180 fileLUT[i] = str 181 } 182 return fileLUT 183} 184