// UNREVIEWED // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package noder import ( "encoding/binary" "fmt" "go/constant" "go/token" "math/big" "os" "runtime" "strings" "cmd/compile/internal/base" ) type pkgDecoder struct { pkgPath string elemEndsEnds [numRelocs]uint32 elemEnds []uint32 elemData string } func newPkgDecoder(pkgPath, input string) pkgDecoder { pr := pkgDecoder{ pkgPath: pkgPath, } // TODO(mdempsky): Implement direct indexing of input string to // avoid copying the position information. r := strings.NewReader(input) assert(binary.Read(r, binary.LittleEndian, pr.elemEndsEnds[:]) == nil) pr.elemEnds = make([]uint32, pr.elemEndsEnds[len(pr.elemEndsEnds)-1]) assert(binary.Read(r, binary.LittleEndian, pr.elemEnds[:]) == nil) pos, err := r.Seek(0, os.SEEK_CUR) assert(err == nil) pr.elemData = input[pos:] assert(len(pr.elemData) == int(pr.elemEnds[len(pr.elemEnds)-1])) return pr } func (pr *pkgDecoder) numElems(k reloc) int { count := int(pr.elemEndsEnds[k]) if k > 0 { count -= int(pr.elemEndsEnds[k-1]) } return count } func (pr *pkgDecoder) totalElems() int { return len(pr.elemEnds) } func (pr *pkgDecoder) absIdx(k reloc, idx int) int { absIdx := idx if k > 0 { absIdx += int(pr.elemEndsEnds[k-1]) } if absIdx >= int(pr.elemEndsEnds[k]) { base.Fatalf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds) } return absIdx } func (pr *pkgDecoder) dataIdx(k reloc, idx int) string { absIdx := pr.absIdx(k, idx) var start uint32 if absIdx > 0 { start = pr.elemEnds[absIdx-1] } end := pr.elemEnds[absIdx] return pr.elemData[start:end] } func (pr *pkgDecoder) stringIdx(idx int) string { return pr.dataIdx(relocString, idx) } func (pr *pkgDecoder) newDecoder(k reloc, idx int, marker syncMarker) decoder { r := pr.newDecoderRaw(k, idx) r.sync(marker) return r } func (pr *pkgDecoder) newDecoderRaw(k reloc, idx int) decoder { r := decoder{ common: pr, k: k, idx: idx, } // TODO(mdempsky) r.data.Reset(...) after #44505 is resolved. r.data = *strings.NewReader(pr.dataIdx(k, idx)) r.sync(syncRelocs) r.relocs = make([]relocEnt, r.len()) for i := range r.relocs { r.sync(syncReloc) r.relocs[i] = relocEnt{reloc(r.len()), r.len()} } return r } type decoder struct { common *pkgDecoder relocs []relocEnt data strings.Reader k reloc idx int } func (r *decoder) checkErr(err error) { if err != nil { base.Fatalf("unexpected error: %v", err) } } func (r *decoder) rawUvarint() uint64 { x, err := binary.ReadUvarint(&r.data) r.checkErr(err) return x } func (r *decoder) rawVarint() int64 { ux := r.rawUvarint() // Zig-zag decode. x := int64(ux >> 1) if ux&1 != 0 { x = ^x } return x } func (r *decoder) rawReloc(k reloc, idx int) int { e := r.relocs[idx] assert(e.kind == k) return e.idx } func (r *decoder) sync(mWant syncMarker) { if !enableSync { return } pos, _ := r.data.Seek(0, os.SEEK_CUR) // TODO(mdempsky): io.SeekCurrent after #44505 is resolved mHave := syncMarker(r.rawUvarint()) writerPCs := make([]int, r.rawUvarint()) for i := range writerPCs { writerPCs[i] = int(r.rawUvarint()) } if mHave == mWant { return } // There's some tension here between printing: // // (1) full file paths that tools can recognize (e.g., so emacs // hyperlinks the "file:line" text for easy navigation), or // // (2) short file paths that are easier for humans to read (e.g., by // omitting redundant or irrelevant details, so it's easier to // focus on the useful bits that remain). // // The current formatting favors the former, as it seems more // helpful in practice. But perhaps the formatting could be improved // to better address both concerns. For example, use relative file // paths if they would be shorter, or rewrite file paths to contain // "$GOROOT" (like objabi.AbsFile does) if tools can be taught how // to reliably expand that again. fmt.Printf("export data desync: package %q, section %v, index %v, offset %v\n", r.common.pkgPath, r.k, r.idx, pos) fmt.Printf("\nfound %v, written at:\n", mHave) if len(writerPCs) == 0 { fmt.Printf("\t[stack trace unavailable; recompile package %q with -d=syncframes]\n", r.common.pkgPath) } for _, pc := range writerPCs { fmt.Printf("\t%s\n", r.common.stringIdx(r.rawReloc(relocString, pc))) } fmt.Printf("\nexpected %v, reading at:\n", mWant) var readerPCs [32]uintptr // TODO(mdempsky): Dynamically size? n := runtime.Callers(2, readerPCs[:]) for _, pc := range fmtFrames(readerPCs[:n]...) { fmt.Printf("\t%s\n", pc) } // We already printed a stack trace for the reader, so now we can // simply exit. Printing a second one with panic or base.Fatalf // would just be noise. os.Exit(1) } func (r *decoder) bool() bool { r.sync(syncBool) x, err := r.data.ReadByte() r.checkErr(err) assert(x < 2) return x != 0 } func (r *decoder) int64() int64 { r.sync(syncInt64) return r.rawVarint() } func (r *decoder) uint64() uint64 { r.sync(syncUint64) return r.rawUvarint() } func (r *decoder) len() int { x := r.uint64(); v := int(x); assert(uint64(v) == x); return v } func (r *decoder) int() int { x := r.int64(); v := int(x); assert(int64(v) == x); return v } func (r *decoder) uint() uint { x := r.uint64(); v := uint(x); assert(uint64(v) == x); return v } func (r *decoder) code(mark syncMarker) int { r.sync(mark) return r.len() } func (r *decoder) reloc(k reloc) int { r.sync(syncUseReloc) return r.rawReloc(k, r.len()) } func (r *decoder) string() string { r.sync(syncString) return r.common.stringIdx(r.reloc(relocString)) } func (r *decoder) strings() []string { res := make([]string, r.len()) for i := range res { res[i] = r.string() } return res } func (r *decoder) rawValue() constant.Value { isComplex := r.bool() val := r.scalar() if isComplex { val = constant.BinaryOp(val, token.ADD, constant.MakeImag(r.scalar())) } return val } func (r *decoder) scalar() constant.Value { switch tag := codeVal(r.code(syncVal)); tag { default: panic(fmt.Sprintf("unexpected scalar tag: %v", tag)) case valBool: return constant.MakeBool(r.bool()) case valString: return constant.MakeString(r.string()) case valInt64: return constant.MakeInt64(r.int64()) case valBigInt: return constant.Make(r.bigInt()) case valBigRat: num := r.bigInt() denom := r.bigInt() return constant.Make(new(big.Rat).SetFrac(num, denom)) case valBigFloat: return constant.Make(r.bigFloat()) } } func (r *decoder) bigInt() *big.Int { v := new(big.Int).SetBytes([]byte(r.string())) if r.bool() { v.Neg(v) } return v } func (r *decoder) bigFloat() *big.Float { v := new(big.Float).SetPrec(512) assert(v.UnmarshalText([]byte(r.string())) == nil) return v }