GoPLS Viewer

Home|gopls/internal/pkgbits/decoder.go
1// Copyright 2021 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
5package pkgbits
6
7import (
8    "encoding/binary"
9    "errors"
10    "fmt"
11    "go/constant"
12    "go/token"
13    "io"
14    "math/big"
15    "os"
16    "runtime"
17    "strings"
18)
19
20// A PkgDecoder provides methods for decoding a package's Unified IR
21// export data.
22type PkgDecoder struct {
23    // version is the file format version.
24    version uint32
25
26    // sync indicates whether the file uses sync markers.
27    sync bool
28
29    // pkgPath is the package path for the package to be decoded.
30    //
31    // TODO(mdempsky): Remove; unneeded since CL 391014.
32    pkgPath string
33
34    // elemData is the full data payload of the encoded package.
35    // Elements are densely and contiguously packed together.
36    //
37    // The last 8 bytes of elemData are the package fingerprint.
38    elemData string
39
40    // elemEnds stores the byte-offset end positions of element
41    // bitstreams within elemData.
42    //
43    // For example, element I's bitstream data starts at elemEnds[I-1]
44    // (or 0, if I==0) and ends at elemEnds[I].
45    //
46    // Note: elemEnds is indexed by absolute indices, not
47    // section-relative indices.
48    elemEnds []uint32
49
50    // elemEndsEnds stores the index-offset end positions of relocation
51    // sections within elemEnds.
52    //
53    // For example, section K's end positions start at elemEndsEnds[K-1]
54    // (or 0, if K==0) and end at elemEndsEnds[K].
55    elemEndsEnds [numRelocs]uint32
56
57    scratchRelocEnt []RelocEnt
58}
59
60// PkgPath returns the package path for the package
61//
62// TODO(mdempsky): Remove; unneeded since CL 391014.
63func (pr *PkgDecoderPkgPath() string { return pr.pkgPath }
64
65// SyncMarkers reports whether pr uses sync markers.
66func (pr *PkgDecoderSyncMarkers() bool { return pr.sync }
67
68// NewPkgDecoder returns a PkgDecoder initialized to read the Unified
69// IR export data from input. pkgPath is the package path for the
70// compilation unit that produced the export data.
71//
72// TODO(mdempsky): Remove pkgPath parameter; unneeded since CL 391014.
73func NewPkgDecoder(pkgPathinput stringPkgDecoder {
74    pr := PkgDecoder{
75        pkgPathpkgPath,
76    }
77
78    // TODO(mdempsky): Implement direct indexing of input string to
79    // avoid copying the position information.
80
81    r := strings.NewReader(input)
82
83    assert(binary.Read(rbinary.LittleEndian, &pr.version) == nil)
84
85    switch pr.version {
86    default:
87        panic(fmt.Errorf("unsupported version: %v"pr.version))
88    case 0:
89        // no flags
90    case 1:
91        var flags uint32
92        assert(binary.Read(rbinary.LittleEndian, &flags) == nil)
93        pr.sync = flags&flagSyncMarkers != 0
94    }
95
96    assert(binary.Read(rbinary.LittleEndianpr.elemEndsEnds[:]) == nil)
97
98    pr.elemEnds = make([]uint32pr.elemEndsEnds[len(pr.elemEndsEnds)-1])
99    assert(binary.Read(rbinary.LittleEndianpr.elemEnds[:]) == nil)
100
101    poserr := r.Seek(0io.SeekCurrent)
102    assert(err == nil)
103
104    pr.elemData = input[pos:]
105    assert(len(pr.elemData)-8 == int(pr.elemEnds[len(pr.elemEnds)-1]))
106
107    return pr
108}
109
110// NumElems returns the number of elements in section k.
111func (pr *PkgDecoderNumElems(k RelocKindint {
112    count := int(pr.elemEndsEnds[k])
113    if k > 0 {
114        count -= int(pr.elemEndsEnds[k-1])
115    }
116    return count
117}
118
119// TotalElems returns the total number of elements across all sections.
120func (pr *PkgDecoderTotalElems() int {
121    return len(pr.elemEnds)
122}
123
124// Fingerprint returns the package fingerprint.
125func (pr *PkgDecoderFingerprint() [8]byte {
126    var fp [8]byte
127    copy(fp[:], pr.elemData[len(pr.elemData)-8:])
128    return fp
129}
130
131// AbsIdx returns the absolute index for the given (section, index)
132// pair.
133func (pr *PkgDecoderAbsIdx(k RelocKindidx Indexint {
134    absIdx := int(idx)
135    if k > 0 {
136        absIdx += int(pr.elemEndsEnds[k-1])
137    }
138    if absIdx >= int(pr.elemEndsEnds[k]) {
139        errorf("%v:%v is out of bounds; %v"kidxpr.elemEndsEnds)
140    }
141    return absIdx
142}
143
144// DataIdx returns the raw element bitstream for the given (section,
145// index) pair.
146func (pr *PkgDecoderDataIdx(k RelocKindidx Indexstring {
147    absIdx := pr.AbsIdx(kidx)
148
149    var start uint32
150    if absIdx > 0 {
151        start = pr.elemEnds[absIdx-1]
152    }
153    end := pr.elemEnds[absIdx]
154
155    return pr.elemData[start:end]
156}
157
158// StringIdx returns the string value for the given string index.
159func (pr *PkgDecoderStringIdx(idx Indexstring {
160    return pr.DataIdx(RelocStringidx)
161}
162
163// NewDecoder returns a Decoder for the given (section, index) pair,
164// and decodes the given SyncMarker from the element bitstream.
165func (pr *PkgDecoderNewDecoder(k RelocKindidx Indexmarker SyncMarkerDecoder {
166    r := pr.NewDecoderRaw(kidx)
167    r.Sync(marker)
168    return r
169}
170
171// TempDecoder returns a Decoder for the given (section, index) pair,
172// and decodes the given SyncMarker from the element bitstream.
173// If possible the Decoder should be RetireDecoder'd when it is no longer
174// needed, this will avoid heap allocations.
175func (pr *PkgDecoderTempDecoder(k RelocKindidx Indexmarker SyncMarkerDecoder {
176    r := pr.TempDecoderRaw(kidx)
177    r.Sync(marker)
178    return r
179}
180
181func (pr *PkgDecoderRetireDecoder(d *Decoder) {
182    pr.scratchRelocEnt = d.Relocs
183    d.Relocs = nil
184}
185
186// NewDecoderRaw returns a Decoder for the given (section, index) pair.
187//
188// Most callers should use NewDecoder instead.
189func (pr *PkgDecoderNewDecoderRaw(k RelocKindidx IndexDecoder {
190    r := Decoder{
191        commonpr,
192        k:      k,
193        Idx:    idx,
194    }
195
196    // TODO(mdempsky) r.data.Reset(...) after #44505 is resolved.
197    r.Data = *strings.NewReader(pr.DataIdx(kidx))
198
199    r.Sync(SyncRelocs)
200    r.Relocs = make([]RelocEntr.Len())
201    for i := range r.Relocs {
202        r.Sync(SyncReloc)
203        r.Relocs[i] = RelocEnt{RelocKind(r.Len()), Index(r.Len())}
204    }
205
206    return r
207}
208
209func (pr *PkgDecoderTempDecoderRaw(k RelocKindidx IndexDecoder {
210    r := Decoder{
211        commonpr,
212        k:      k,
213        Idx:    idx,
214    }
215
216    r.Data.Reset(pr.DataIdx(kidx))
217    r.Sync(SyncRelocs)
218    l := r.Len()
219    if cap(pr.scratchRelocEnt) >= l {
220        r.Relocs = pr.scratchRelocEnt[:l]
221        pr.scratchRelocEnt = nil
222    } else {
223        r.Relocs = make([]RelocEntl)
224    }
225    for i := range r.Relocs {
226        r.Sync(SyncReloc)
227        r.Relocs[i] = RelocEnt{RelocKind(r.Len()), Index(r.Len())}
228    }
229
230    return r
231}
232
233// A Decoder provides methods for decoding an individual element's
234// bitstream data.
235type Decoder struct {
236    common *PkgDecoder
237
238    Relocs []RelocEnt
239    Data   strings.Reader
240
241    k   RelocKind
242    Idx Index
243}
244
245func (r *DecodercheckErr(err error) {
246    if err != nil {
247        errorf("unexpected decoding error: %w"err)
248    }
249}
250
251func (r *DecoderrawUvarint() uint64 {
252    xerr := readUvarint(&r.Data)
253    r.checkErr(err)
254    return x
255}
256
257// readUvarint is a type-specialized copy of encoding/binary.ReadUvarint.
258// This avoids the interface conversion and thus has better escape properties,
259// which flows up the stack.
260func readUvarint(r *strings.Reader) (uint64error) {
261    var x uint64
262    var s uint
263    for i := 0i < binary.MaxVarintLen64i++ {
264        berr := r.ReadByte()
265        if err != nil {
266            if i > 0 && err == io.EOF {
267                err = io.ErrUnexpectedEOF
268            }
269            return xerr
270        }
271        if b < 0x80 {
272            if i == binary.MaxVarintLen64-1 && b > 1 {
273                return xoverflow
274            }
275            return x | uint64(b)<<snil
276        }
277        x |= uint64(b&0x7f) << s
278        s += 7
279    }
280    return xoverflow
281}
282
283var overflow = errors.New("pkgbits: readUvarint overflows a 64-bit integer")
284
285func (r *DecoderrawVarint() int64 {
286    ux := r.rawUvarint()
287
288    // Zig-zag decode.
289    x := int64(ux >> 1)
290    if ux&1 != 0 {
291        x = ^x
292    }
293    return x
294}
295
296func (r *DecoderrawReloc(k RelocKindidx intIndex {
297    e := r.Relocs[idx]
298    assert(e.Kind == k)
299    return e.Idx
300}
301
302// Sync decodes a sync marker from the element bitstream and asserts
303// that it matches the expected marker.
304//
305// If r.common.sync is false, then Sync is a no-op.
306func (r *DecoderSync(mWant SyncMarker) {
307    if !r.common.sync {
308        return
309    }
310
311    pos_ := r.Data.Seek(0io.SeekCurrent)
312    mHave := SyncMarker(r.rawUvarint())
313    writerPCs := make([]intr.rawUvarint())
314    for i := range writerPCs {
315        writerPCs[i] = int(r.rawUvarint())
316    }
317
318    if mHave == mWant {
319        return
320    }
321
322    // There's some tension here between printing:
323    //
324    // (1) full file paths that tools can recognize (e.g., so emacs
325    //     hyperlinks the "file:line" text for easy navigation), or
326    //
327    // (2) short file paths that are easier for humans to read (e.g., by
328    //     omitting redundant or irrelevant details, so it's easier to
329    //     focus on the useful bits that remain).
330    //
331    // The current formatting favors the former, as it seems more
332    // helpful in practice. But perhaps the formatting could be improved
333    // to better address both concerns. For example, use relative file
334    // paths if they would be shorter, or rewrite file paths to contain
335    // "$GOROOT" (like objabi.AbsFile does) if tools can be taught how
336    // to reliably expand that again.
337
338    fmt.Printf("export data desync: package %q, section %v, index %v, offset %v\n"r.common.pkgPathr.kr.Idxpos)
339
340    fmt.Printf("\nfound %v, written at:\n"mHave)
341    if len(writerPCs) == 0 {
342        fmt.Printf("\t[stack trace unavailable; recompile package %q with -d=syncframes]\n"r.common.pkgPath)
343    }
344    for _pc := range writerPCs {
345        fmt.Printf("\t%s\n"r.common.StringIdx(r.rawReloc(RelocStringpc)))
346    }
347
348    fmt.Printf("\nexpected %v, reading at:\n"mWant)
349    var readerPCs [32]uintptr // TODO(mdempsky): Dynamically size?
350    n := runtime.Callers(2readerPCs[:])
351    for _pc := range fmtFrames(readerPCs[:n]...) {
352        fmt.Printf("\t%s\n"pc)
353    }
354
355    // We already printed a stack trace for the reader, so now we can
356    // simply exit. Printing a second one with panic or base.Fatalf
357    // would just be noise.
358    os.Exit(1)
359}
360
361// Bool decodes and returns a bool value from the element bitstream.
362func (r *DecoderBool() bool {
363    r.Sync(SyncBool)
364    xerr := r.Data.ReadByte()
365    r.checkErr(err)
366    assert(x < 2)
367    return x != 0
368}
369
370// Int64 decodes and returns an int64 value from the element bitstream.
371func (r *DecoderInt64() int64 {
372    r.Sync(SyncInt64)
373    return r.rawVarint()
374}
375
376// Int64 decodes and returns a uint64 value from the element bitstream.
377func (r *DecoderUint64() uint64 {
378    r.Sync(SyncUint64)
379    return r.rawUvarint()
380}
381
382// Len decodes and returns a non-negative int value from the element bitstream.
383func (r *DecoderLen() int { x := r.Uint64(); v := int(x); assert(uint64(v) == x); return v }
384
385// Int decodes and returns an int value from the element bitstream.
386func (r *DecoderInt() int { x := r.Int64(); v := int(x); assert(int64(v) == x); return v }
387
388// Uint decodes and returns a uint value from the element bitstream.
389func (r *DecoderUint() uint { x := r.Uint64(); v := uint(x); assert(uint64(v) == x); return v }
390
391// Code decodes a Code value from the element bitstream and returns
392// its ordinal value. It's the caller's responsibility to convert the
393// result to an appropriate Code type.
394//
395// TODO(mdempsky): Ideally this method would have signature "Code[T
396// Code] T" instead, but we don't allow generic methods and the
397// compiler can't depend on generics yet anyway.
398func (r *DecoderCode(mark SyncMarkerint {
399    r.Sync(mark)
400    return r.Len()
401}
402
403// Reloc decodes a relocation of expected section k from the element
404// bitstream and returns an index to the referenced element.
405func (r *DecoderReloc(k RelocKindIndex {
406    r.Sync(SyncUseReloc)
407    return r.rawReloc(kr.Len())
408}
409
410// String decodes and returns a string value from the element
411// bitstream.
412func (r *DecoderString() string {
413    r.Sync(SyncString)
414    return r.common.StringIdx(r.Reloc(RelocString))
415}
416
417// Strings decodes and returns a variable-length slice of strings from
418// the element bitstream.
419func (r *DecoderStrings() []string {
420    res := make([]stringr.Len())
421    for i := range res {
422        res[i] = r.String()
423    }
424    return res
425}
426
427// Value decodes and returns a constant.Value from the element
428// bitstream.
429func (r *DecoderValue() constant.Value {
430    r.Sync(SyncValue)
431    isComplex := r.Bool()
432    val := r.scalar()
433    if isComplex {
434        val = constant.BinaryOp(valtoken.ADDconstant.MakeImag(r.scalar()))
435    }
436    return val
437}
438
439func (r *Decoderscalar() constant.Value {
440    switch tag := CodeVal(r.Code(SyncVal)); tag {
441    default:
442        panic(fmt.Errorf("unexpected scalar tag: %v"tag))
443
444    case ValBool:
445        return constant.MakeBool(r.Bool())
446    case ValString:
447        return constant.MakeString(r.String())
448    case ValInt64:
449        return constant.MakeInt64(r.Int64())
450    case ValBigInt:
451        return constant.Make(r.bigInt())
452    case ValBigRat:
453        num := r.bigInt()
454        denom := r.bigInt()
455        return constant.Make(new(big.Rat).SetFrac(numdenom))
456    case ValBigFloat:
457        return constant.Make(r.bigFloat())
458    }
459}
460
461func (r *DecoderbigInt() *big.Int {
462    v := new(big.Int).SetBytes([]byte(r.String()))
463    if r.Bool() {
464        v.Neg(v)
465    }
466    return v
467}
468
469func (r *DecoderbigFloat() *big.Float {
470    v := new(big.Float).SetPrec(512)
471    assert(v.UnmarshalText([]byte(r.String())) == nil)
472    return v
473}
474
475// @@@ Helpers
476
477// TODO(mdempsky): These should probably be removed. I think they're a
478// smell that the export data format is not yet quite right.
479
480// PeekPkgPath returns the package path for the specified package
481// index.
482func (pr *PkgDecoderPeekPkgPath(idx Indexstring {
483    var path string
484    {
485        r := pr.TempDecoder(RelocPkgidxSyncPkgDef)
486        path = r.String()
487        pr.RetireDecoder(&r)
488    }
489    if path == "" {
490        path = pr.pkgPath
491    }
492    return path
493}
494
495// PeekObj returns the package path, object name, and CodeObj for the
496// specified object index.
497func (pr *PkgDecoderPeekObj(idx Index) (stringstringCodeObj) {
498    var ridx Index
499    var name string
500    var rcode int
501    {
502        r := pr.TempDecoder(RelocNameidxSyncObject1)
503        r.Sync(SyncSym)
504        r.Sync(SyncPkg)
505        ridx = r.Reloc(RelocPkg)
506        name = r.String()
507        rcode = r.Code(SyncCodeObj)
508        pr.RetireDecoder(&r)
509    }
510
511    path := pr.PeekPkgPath(ridx)
512    assert(name != "")
513
514    tag := CodeObj(rcode)
515
516    return pathnametag
517}
518
MembersX
Decoder.Data
Decoder.Code.r
PkgDecoder.Fingerprint.fp
Decoder.Len.v
PkgDecoder.PeekObj.idx
PkgDecoder.SyncMarkers.pr
PkgDecoder.PeekObj.BlockStmt.r
Decoder.Len.r
Decoder.Int
Decoder.Uint.x
PkgDecoder.AbsIdx
PkgDecoder.TempDecoder.marker
PkgDecoder.RetireDecoder.d
Decoder.Sync.r
Decoder.Uint.r
Decoder.Reloc.r
io
NewPkgDecoder.pkgPath
PkgDecoder.TempDecoderRaw.RangeStmt_5958.i
Decoder.Bool.x
PkgDecoder.PeekObj
binary
fmt
PkgDecoder.TempDecoderRaw.k
readUvarint.r
readUvarint.i
Decoder.rawReloc.idx
Decoder.Bool
Decoder.scalar
os
NewPkgDecoder
PkgDecoder.TempDecoderRaw
Decoder.String.r
Decoder.String
Decoder.bigInt.r
Decoder.Code
Decoder.bigInt.v
Decoder.bigFloat.v
PkgDecoder.PeekObj.tag
Decoder.checkErr.r
Decoder.checkErr
Decoder.Sync.pos
PkgDecoder.NewDecoderRaw.idx
Decoder.Sync.RangeStmt_8971.pc
Decoder.Int.r
Decoder.Reloc.k
Decoder.Value.val
PkgDecoder.StringIdx.pr
PkgDecoder.TempDecoder.idx
PkgDecoder.NewDecoderRaw.k
PkgDecoder.PeekPkgPath.idx
Decoder.Sync
Decoder.Bool.err
Decoder.Strings.RangeStmt_11517.i
Decoder.bigInt
strings
PkgDecoder.TempDecoderRaw.pr
Decoder.rawUvarint.x
PkgDecoder.NewDecoderRaw.RangeStmt_5489.i
Decoder.rawUvarint.r
Decoder.rawReloc.r
PkgDecoder.PeekPkgPath.path
PkgDecoder.PeekObj.pr
big
PkgDecoder.version
PkgDecoder.TempDecoder
Decoder.rawVarint.r
Decoder.Sync.mWant
PkgDecoder.PeekPkgPath.pr
PkgDecoder.PeekObj.rcode
PkgDecoder.TotalElems.pr
PkgDecoder.StringIdx
readUvarint.s
PkgDecoder.NewDecoder
PkgDecoder.NewDecoderRaw.pr
Decoder.rawVarint
Decoder.Sync.readerPCs
Decoder.Code.mark
PkgDecoder.pkgPath
PkgDecoder.AbsIdx.absIdx
PkgDecoder.DataIdx.k
Decoder.Value.r
PkgDecoder.PeekPkgPath.BlockStmt.r
Decoder.rawReloc.k
Decoder.Len.x
Decoder.Strings.res
Decoder.scalar.BlockStmt.denom
PkgDecoder.NewDecoder.idx
PkgDecoder.NewDecoder.marker
Decoder.Uint64
Decoder.Sync.n
Decoder.Len
Decoder.bigFloat
PkgDecoder.SyncMarkers
PkgDecoder.DataIdx.absIdx
PkgDecoder.TempDecoder.k
PkgDecoder.TempDecoder.pr
PkgDecoder.RetireDecoder.pr
PkgDecoder.NewDecoderRaw.r
Decoder.Idx
PkgDecoder.PeekObj.path
token
PkgDecoder.elemData
PkgDecoder.NewDecoder.k
PkgDecoder.AbsIdx.k
PkgDecoder.TempDecoder.r
readUvarint.BlockStmt.b
Decoder.Int64.r
Decoder.scalar.tag
NewPkgDecoder.input
NewPkgDecoder.pos
PkgDecoder.NumElems.count
readUvarint
Decoder.Sync._
Decoder.Uint
Decoder.Strings.r
PkgDecoder.AbsIdx.pr
PkgDecoder.DataIdx
Decoder.k
PkgDecoder.StringIdx.idx
Decoder
Decoder.rawUvarint
Decoder.Sync.RangeStmt_7820.i
Decoder.Int.v
constant
PkgDecoder
PkgDecoder.DataIdx.idx
Decoder.scalar.r
Decoder.scalar.BlockStmt.num
PkgDecoder.PeekPkgPath
readUvarint.x
Decoder.Int.x
Decoder.Sync.RangeStmt_9233.pc
Decoder.Uint.v
Decoder.Value
NewPkgDecoder.BlockStmt.flags
PkgDecoder.Fingerprint.pr
Decoder.Relocs
readUvarint.BlockStmt.err
PkgDecoder.Fingerprint
PkgDecoder.DataIdx.pr
PkgDecoder.DataIdx.start
PkgDecoder.sync
Decoder.Bool.r
Decoder.Uint64.r
Decoder.checkErr.err
Decoder.Sync.writerPCs
Decoder.Strings
Decoder.bigFloat.r
PkgDecoder.PeekObj.name
PkgDecoder.scratchRelocEnt
PkgDecoder.AbsIdx.idx
PkgDecoder.NewDecoderRaw
PkgDecoder.TempDecoderRaw.r
Decoder.common
Decoder.rawVarint.x
Decoder.Int64
Decoder.Reloc
NewPkgDecoder.pr
PkgDecoder.NumElems.pr
PkgDecoder.TempDecoderRaw.idx
PkgDecoder.TotalElems
PkgDecoder.NewDecoder.pr
PkgDecoder.RetireDecoder
Decoder.rawUvarint.err
Decoder.rawVarint.ux
runtime
PkgDecoder.PkgPath.pr
NewPkgDecoder.r
Decoder.Sync.mHave
Decoder.Value.isComplex
PkgDecoder.PkgPath
PkgDecoder.NewDecoder.r
Decoder.rawReloc
PkgDecoder.NumElems
PkgDecoder.NumElems.k
PkgDecoder.PeekObj.ridx
PkgDecoder.elemEnds
PkgDecoder.elemEndsEnds
NewPkgDecoder.err
errors
PkgDecoder.TempDecoderRaw.l
Members
X