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 8 package src
9 10 // XPos is a more compact representation of Pos.
11 type XPos struct {
12 index int32
13 lico
14 }
15 16 // NoXPos is a valid unknown position.
17 var NoXPos XPos
18 19 // IsKnown reports whether the position p is known.
20 // XPos.IsKnown() matches Pos.IsKnown() for corresponding
21 // positions.
22 func (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.
28 func (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.
34 func (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.
39 func (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.
45 func (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
51 func (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
57 func (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
63 func (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.
73 func (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
83 func (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.
89 func (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.
99 func (p XPos) FileIndex() int32 {
100 return p.index
101 }
102 103 func (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.
111 func (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.
118 type 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.
126 func (t *PosTable) XPos(pos Pos) XPos {
127 m := t.indexMap
128 if m == nil {
129 // Create new list and map and populate with nil
130 // base so that NoPos always gets index 0.
131 t.baseList = append(t.baseList, nil)
132 m = map[*PosBase]int{nil: 0}
133 t.indexMap = m
134 t.nameMap = make(map[string]int)
135 }
136 i, ok := m[pos.base]
137 if !ok {
138 i = len(t.baseList)
139 t.baseList = append(t.baseList, pos.base)
140 t.indexMap[pos.base] = i
141 if _, ok := t.nameMap[pos.base.symFilename]; !ok {
142 t.nameMap[pos.base.symFilename] = len(t.nameMap)
143 }
144 }
145 return XPos{int32(i), pos.lico}
146 }
147 148 // Pos returns the corresponding Pos for the given p.
149 // If p cannot be translated via t, the function panics.
150 func (t *PosTable) Pos(p XPos) Pos {
151 var base *PosBase
152 if p.index != 0 {
153 base = t.baseList[p.index]
154 }
155 return Pos{base, p.lico}
156 }
157 158 // FileIndex returns the index of the given filename(symbol) in the PosTable, or -1 if not found.
159 func (t *PosTable) FileIndex(filename string) int {
160 if v, ok := t.nameMap[filename]; ok {
161 return v
162 }
163 return -1
164 }
165 166 // FileTable returns a slice of all files used to build this package.
167 func (t *PosTable) FileTable() []string {
168 // Create a LUT of the global package level file indices. This table is what
169 // is written in the debug_lines header, the file[N] will be referenced as
170 // N+1 in the debug_lines table.
171 fileLUT := make([]string, len(t.nameMap))
172 for str, i := range t.nameMap {
173 fileLUT[i] = str
174 }
175 return fileLUT
176 }
177