podman

Форк
0
470 строк · 15.4 Кб
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 encoding of source positions.
6

7
package src
8

9
import (
10
	"bytes"
11
	"fmt"
12
	"io"
13
)
14

15
// A Pos encodes a source position consisting of a (line, column) number pair
16
// and a position base. A zero Pos is a ready to use "unknown" position (nil
17
// position base and zero line number).
18
//
19
// The (line, column) values refer to a position in a file independent of any
20
// position base ("absolute" file position).
21
//
22
// The position base is used to determine the "relative" position, that is the
23
// filename and line number relative to the position base. If the base refers
24
// to the current file, there is no difference between absolute and relative
25
// positions. If it refers to a //line directive, a relative position is relative
26
// to that directive. A position base in turn contains the position at which it
27
// was introduced in the current file.
28
type Pos struct {
29
	base *PosBase
30
	lico
31
}
32

33
// NoPos is a valid unknown position.
34
var NoPos Pos
35

36
// MakePos creates a new Pos value with the given base, and (file-absolute)
37
// line and column.
38
func MakePos(base *PosBase, line, col uint) Pos {
39
	return Pos{base, makeLico(line, col)}
40
}
41

42
// IsKnown reports whether the position p is known.
43
// A position is known if it either has a non-nil
44
// position base, or a non-zero line number.
45
func (p Pos) IsKnown() bool {
46
	return p.base != nil || p.Line() != 0
47
}
48

49
// Before reports whether the position p comes before q in the source.
50
// For positions in different files, ordering is by filename.
51
func (p Pos) Before(q Pos) bool {
52
	n, m := p.Filename(), q.Filename()
53
	return n < m || n == m && p.lico < q.lico
54
}
55

56
// After reports whether the position p comes after q in the source.
57
// For positions in different files, ordering is by filename.
58
func (p Pos) After(q Pos) bool {
59
	n, m := p.Filename(), q.Filename()
60
	return n > m || n == m && p.lico > q.lico
61
}
62

63
func (p Pos) LineNumber() string {
64
	if !p.IsKnown() {
65
		return "?"
66
	}
67
	return p.lico.lineNumber()
68
}
69

70
func (p Pos) LineNumberHTML() string {
71
	if !p.IsKnown() {
72
		return "?"
73
	}
74
	return p.lico.lineNumberHTML()
75
}
76

77
// Filename returns the name of the actual file containing this position.
78
func (p Pos) Filename() string { return p.base.Pos().RelFilename() }
79

80
// Base returns the position base.
81
func (p Pos) Base() *PosBase { return p.base }
82

83
// SetBase sets the position base.
84
func (p *Pos) SetBase(base *PosBase) { p.base = base }
85

86
// RelFilename returns the filename recorded with the position's base.
87
func (p Pos) RelFilename() string { return p.base.Filename() }
88

89
// RelLine returns the line number relative to the position's base.
90
func (p Pos) RelLine() uint {
91
	b := p.base
92
	if b.Line() == 0 {
93
		// base line is unknown => relative line is unknown
94
		return 0
95
	}
96
	return b.Line() + (p.Line() - b.Pos().Line())
97
}
98

99
// RelCol returns the column number relative to the position's base.
100
func (p Pos) RelCol() uint {
101
	b := p.base
102
	if b.Col() == 0 {
103
		// base column is unknown => relative column is unknown
104
		// (the current specification for line directives requires
105
		// this to apply until the next PosBase/line directive,
106
		// not just until the new newline)
107
		return 0
108
	}
109
	if p.Line() == b.Pos().Line() {
110
		// p on same line as p's base => column is relative to p's base
111
		return b.Col() + (p.Col() - b.Pos().Col())
112
	}
113
	return p.Col()
114
}
115

116
// AbsFilename() returns the absolute filename recorded with the position's base.
117
func (p Pos) AbsFilename() string { return p.base.AbsFilename() }
118

119
// SymFilename() returns the absolute filename recorded with the position's base,
120
// prefixed by FileSymPrefix to make it appropriate for use as a linker symbol.
121
func (p Pos) SymFilename() string { return p.base.SymFilename() }
122

123
func (p Pos) String() string {
124
	return p.Format(true, true)
125
}
126

127
// Format formats a position as "filename:line" or "filename:line:column",
128
// controlled by the showCol flag and if the column is known (!= 0).
129
// For positions relative to line directives, the original position is
130
// shown as well, as in "filename:line[origfile:origline:origcolumn] if
131
// showOrig is set.
132
func (p Pos) Format(showCol, showOrig bool) string {
133
	buf := new(bytes.Buffer)
134
	p.WriteTo(buf, showCol, showOrig)
135
	return buf.String()
136
}
137

138
// WriteTo a position to w, formatted as Format does.
139
func (p Pos) WriteTo(w io.Writer, showCol, showOrig bool) {
140
	if !p.IsKnown() {
141
		io.WriteString(w, "<unknown line number>")
142
		return
143
	}
144

145
	if b := p.base; b == b.Pos().base {
146
		// base is file base (incl. nil)
147
		format(w, p.Filename(), p.Line(), p.Col(), showCol)
148
		return
149
	}
150

151
	// base is relative
152
	// Print the column only for the original position since the
153
	// relative position's column information may be bogus (it's
154
	// typically generated code and we can't say much about the
155
	// original source at that point but for the file:line info
156
	// that's provided via a line directive).
157
	// TODO(gri) This may not be true if we have an inlining base.
158
	// We may want to differentiate at some point.
159
	format(w, p.RelFilename(), p.RelLine(), p.RelCol(), showCol)
160
	if showOrig {
161
		io.WriteString(w, "[")
162
		format(w, p.Filename(), p.Line(), p.Col(), showCol)
163
		io.WriteString(w, "]")
164
	}
165
}
166

167
// format formats a (filename, line, col) tuple as "filename:line" (showCol
168
// is false or col == 0) or "filename:line:column" (showCol is true and col != 0).
169
func format(w io.Writer, filename string, line, col uint, showCol bool) {
170
	io.WriteString(w, filename)
171
	io.WriteString(w, ":")
172
	fmt.Fprint(w, line)
173
	// col == 0 and col == colMax are interpreted as unknown column values
174
	if showCol && 0 < col && col < colMax {
175
		io.WriteString(w, ":")
176
		fmt.Fprint(w, col)
177
	}
178
}
179

180
// formatstr wraps format to return a string.
181
func formatstr(filename string, line, col uint, showCol bool) string {
182
	buf := new(bytes.Buffer)
183
	format(buf, filename, line, col, showCol)
184
	return buf.String()
185
}
186

187
// ----------------------------------------------------------------------------
188
// PosBase
189

190
// A PosBase encodes a filename and base position.
191
// Typically, each file and line directive introduce a PosBase.
192
type PosBase struct {
193
	pos         Pos    // position at which the relative position is (line, col)
194
	filename    string // file name used to open source file, for error messages
195
	absFilename string // absolute file name, for PC-Line tables
196
	symFilename string // cached symbol file name, to avoid repeated string concatenation
197
	line, col   uint   // relative line, column number at pos
198
	inl         int    // inlining index (see cmd/internal/obj/inl.go)
199
}
200

201
// NewFileBase returns a new *PosBase for a file with the given (relative and
202
// absolute) filenames.
203
func NewFileBase(filename, absFilename string) *PosBase {
204
	base := &PosBase{
205
		filename:    filename,
206
		absFilename: absFilename,
207
		symFilename: FileSymPrefix + absFilename,
208
		line:        1,
209
		col:         1,
210
		inl:         -1,
211
	}
212
	base.pos = MakePos(base, 1, 1)
213
	return base
214
}
215

216
// NewLinePragmaBase returns a new *PosBase for a line directive of the form
217
//      //line filename:line:col
218
//      /*line filename:line:col*/
219
// at position pos.
220
func NewLinePragmaBase(pos Pos, filename, absFilename string, line, col uint) *PosBase {
221
	return &PosBase{pos, filename, absFilename, FileSymPrefix + absFilename, line, col, -1}
222
}
223

224
// NewInliningBase returns a copy of the old PosBase with the given inlining
225
// index. If old == nil, the resulting PosBase has no filename.
226
func NewInliningBase(old *PosBase, inlTreeIndex int) *PosBase {
227
	if old == nil {
228
		base := &PosBase{line: 1, col: 1, inl: inlTreeIndex}
229
		base.pos = MakePos(base, 1, 1)
230
		return base
231
	}
232
	copy := *old
233
	base := &copy
234
	base.inl = inlTreeIndex
235
	if old == old.pos.base {
236
		base.pos.base = base
237
	}
238
	return base
239
}
240

241
var noPos Pos
242

243
// Pos returns the position at which base is located.
244
// If b == nil, the result is the zero position.
245
func (b *PosBase) Pos() *Pos {
246
	if b != nil {
247
		return &b.pos
248
	}
249
	return &noPos
250
}
251

252
// Filename returns the filename recorded with the base.
253
// If b == nil, the result is the empty string.
254
func (b *PosBase) Filename() string {
255
	if b != nil {
256
		return b.filename
257
	}
258
	return ""
259
}
260

261
// AbsFilename returns the absolute filename recorded with the base.
262
// If b == nil, the result is the empty string.
263
func (b *PosBase) AbsFilename() string {
264
	if b != nil {
265
		return b.absFilename
266
	}
267
	return ""
268
}
269

270
const FileSymPrefix = "gofile.."
271

272
// SymFilename returns the absolute filename recorded with the base,
273
// prefixed by FileSymPrefix to make it appropriate for use as a linker symbol.
274
// If b is nil, SymFilename returns FileSymPrefix + "??".
275
func (b *PosBase) SymFilename() string {
276
	if b != nil {
277
		return b.symFilename
278
	}
279
	return FileSymPrefix + "??"
280
}
281

282
// Line returns the line number recorded with the base.
283
// If b == nil, the result is 0.
284
func (b *PosBase) Line() uint {
285
	if b != nil {
286
		return b.line
287
	}
288
	return 0
289
}
290

291
// Col returns the column number recorded with the base.
292
// If b == nil, the result is 0.
293
func (b *PosBase) Col() uint {
294
	if b != nil {
295
		return b.col
296
	}
297
	return 0
298
}
299

300
// InliningIndex returns the index into the global inlining
301
// tree recorded with the base. If b == nil or the base has
302
// not been inlined, the result is < 0.
303
func (b *PosBase) InliningIndex() int {
304
	if b != nil {
305
		return b.inl
306
	}
307
	return -1
308
}
309

310
// ----------------------------------------------------------------------------
311
// lico
312

313
// A lico is a compact encoding of a LIne and COlumn number.
314
type lico uint32
315

316
// Layout constants: 20 bits for line, 8 bits for column, 2 for isStmt, 2 for pro/epilogue
317
// (If this is too tight, we can either make lico 64b wide,
318
// or we can introduce a tiered encoding where we remove column
319
// information as line numbers grow bigger; similar to what gcc
320
// does.)
321
// The bitfield order is chosen to make IsStmt be the least significant
322
// part of a position; its use is to communicate statement edges through
323
// instruction scrambling in code generation, not to impose an order.
324
// TODO: Prologue and epilogue are perhaps better handled as pseudo-ops for the assembler,
325
// because they have almost no interaction with other uses of the position.
326
const (
327
	lineBits, lineMax     = 20, 1<<lineBits - 2
328
	bogusLine             = 1 // Used to disrupt infinite loops to prevent debugger looping
329
	isStmtBits, isStmtMax = 2, 1<<isStmtBits - 1
330
	xlogueBits, xlogueMax = 2, 1<<xlogueBits - 1
331
	colBits, colMax       = 32 - lineBits - xlogueBits - isStmtBits, 1<<colBits - 1
332

333
	isStmtShift = 0
334
	isStmtMask  = isStmtMax << isStmtShift
335
	xlogueShift = isStmtBits + isStmtShift
336
	xlogueMask  = xlogueMax << xlogueShift
337
	colShift    = xlogueBits + xlogueShift
338
	lineShift   = colBits + colShift
339
)
340
const (
341
	// It is expected that the front end or a phase in SSA will usually generate positions tagged with
342
	// PosDefaultStmt, but note statement boundaries with PosIsStmt.  Simple statements will have a single
343
	// boundary; for loops with initialization may have one for their entry and one for their back edge
344
	// (this depends on exactly how the loop is compiled; the intent is to provide a good experience to a
345
	// user debugging a program; the goal is that a breakpoint set on the loop line fires both on entry
346
	// and on iteration).  Proper treatment of non-gofmt input with multiple simple statements on a single
347
	// line is TBD.
348
	//
349
	// Optimizing compilation will move instructions around, and some of these will become known-bad as
350
	// step targets for debugging purposes (examples: register spills and reloads; code generated into
351
	// the entry block; invariant code hoisted out of loops) but those instructions will still have interesting
352
	// positions for profiling purposes. To reflect this these positions will be changed to PosNotStmt.
353
	//
354
	// When the optimizer removes an instruction marked PosIsStmt; it should attempt to find a nearby
355
	// instruction with the same line marked PosDefaultStmt to be the new statement boundary.  I.e., the
356
	// optimizer should make a best-effort to conserve statement boundary positions, and might be enhanced
357
	// to note when a statement boundary is not conserved.
358
	//
359
	// Code cloning, e.g. loop unrolling or loop unswitching, is an exception to the conservation rule
360
	// because a user running a debugger would expect to see breakpoints active in the copies of the code.
361
	//
362
	// In non-optimizing compilation there is still a role for PosNotStmt because of code generation
363
	// into the entry block.  PosIsStmt statement positions should be conserved.
364
	//
365
	// When code generation occurs any remaining default-marked positions are replaced with not-statement
366
	// positions.
367
	//
368
	PosDefaultStmt uint = iota // Default; position is not a statement boundary, but might be if optimization removes the designated statement boundary
369
	PosIsStmt                  // Position is a statement boundary; if optimization removes the corresponding instruction, it should attempt to find a new instruction to be the boundary.
370
	PosNotStmt                 // Position should not be a statement boundary, but line should be preserved for profiling and low-level debugging purposes.
371
)
372

373
type PosXlogue uint
374

375
const (
376
	PosDefaultLogue PosXlogue = iota
377
	PosPrologueEnd
378
	PosEpilogueBegin
379
)
380

381
func makeLicoRaw(line, col uint) lico {
382
	return lico(line<<lineShift | col<<colShift)
383
}
384

385
// This is a not-position that will not be elided.
386
// Depending on the debugger (gdb or delve) it may or may not be displayed.
387
func makeBogusLico() lico {
388
	return makeLicoRaw(bogusLine, 0).withIsStmt()
389
}
390

391
func makeLico(line, col uint) lico {
392
	if line > lineMax {
393
		// cannot represent line, use max. line so we have some information
394
		line = lineMax
395
	}
396
	if col > colMax {
397
		// cannot represent column, use max. column so we have some information
398
		col = colMax
399
	}
400
	// default is not-sure-if-statement
401
	return makeLicoRaw(line, col)
402
}
403

404
func (x lico) Line() uint           { return uint(x) >> lineShift }
405
func (x lico) SameLine(y lico) bool { return 0 == (x^y)&^lico(1<<lineShift-1) }
406
func (x lico) Col() uint            { return uint(x) >> colShift & colMax }
407
func (x lico) IsStmt() uint {
408
	if x == 0 {
409
		return PosNotStmt
410
	}
411
	return uint(x) >> isStmtShift & isStmtMax
412
}
413
func (x lico) Xlogue() PosXlogue {
414
	return PosXlogue(uint(x) >> xlogueShift & xlogueMax)
415
}
416

417
// withNotStmt returns a lico for the same location, but not a statement
418
func (x lico) withNotStmt() lico {
419
	return x.withStmt(PosNotStmt)
420
}
421

422
// withDefaultStmt returns a lico for the same location, with default isStmt
423
func (x lico) withDefaultStmt() lico {
424
	return x.withStmt(PosDefaultStmt)
425
}
426

427
// withIsStmt returns a lico for the same location, tagged as definitely a statement
428
func (x lico) withIsStmt() lico {
429
	return x.withStmt(PosIsStmt)
430
}
431

432
// withLogue attaches a prologue/epilogue attribute to a lico
433
func (x lico) withXlogue(xlogue PosXlogue) lico {
434
	if x == 0 {
435
		if xlogue == 0 {
436
			return x
437
		}
438
		// Normalize 0 to "not a statement"
439
		x = lico(PosNotStmt << isStmtShift)
440
	}
441
	return lico(uint(x) & ^uint(xlogueMax<<xlogueShift) | (uint(xlogue) << xlogueShift))
442
}
443

444
// withStmt returns a lico for the same location with specified is_stmt attribute
445
func (x lico) withStmt(stmt uint) lico {
446
	if x == 0 {
447
		return lico(0)
448
	}
449
	return lico(uint(x) & ^uint(isStmtMax<<isStmtShift) | (stmt << isStmtShift))
450
}
451

452
func (x lico) lineNumber() string {
453
	return fmt.Sprintf("%d", x.Line())
454
}
455

456
func (x lico) lineNumberHTML() string {
457
	if x.IsStmt() == PosDefaultStmt {
458
		return fmt.Sprintf("%d", x.Line())
459
	}
460
	style, pfx := "b", "+"
461
	if x.IsStmt() == PosNotStmt {
462
		style = "s" // /strike not supported in HTML5
463
		pfx = ""
464
	}
465
	return fmt.Sprintf("<%s>%s%d</%s>", style, pfx, x.Line(), style)
466
}
467

468
func (x lico) atColumn1() lico {
469
	return makeLico(x.Line(), 1).withIsStmt()
470
}
471

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.