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	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.
150func (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.
159func (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.
167func (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