Source file src/cmd/internal/src/xpos.go

     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  	return XPos{t.baseIndex(pos.base), pos.lico}
   128  }
   129  
   130  func (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.
   165  func (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.
   174  func (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  

View as plain text