| // 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 ( |
| "bytes" |
| "encoding/binary" |
| "fmt" |
| "go/constant" |
| "io" |
| "math/big" |
| "runtime" |
| |
| "cmd/compile/internal/base" |
| ) |
| |
| type pkgEncoder struct { |
| elems [numRelocs][]string |
| |
| stringsIdx map[string]int |
| } |
| |
| func newPkgEncoder() pkgEncoder { |
| return pkgEncoder{ |
| stringsIdx: make(map[string]int), |
| } |
| } |
| |
| func (pw *pkgEncoder) dump(out io.Writer) { |
| writeUint32 := func(x uint32) { |
| assert(binary.Write(out, binary.LittleEndian, x) == nil) |
| } |
| |
| var sum uint32 |
| for _, elems := range &pw.elems { |
| sum += uint32(len(elems)) |
| writeUint32(sum) |
| } |
| |
| sum = 0 |
| for _, elems := range &pw.elems { |
| for _, elem := range elems { |
| sum += uint32(len(elem)) |
| writeUint32(sum) |
| } |
| } |
| |
| for _, elems := range &pw.elems { |
| for _, elem := range elems { |
| _, err := io.WriteString(out, elem) |
| assert(err == nil) |
| } |
| } |
| } |
| |
| func (pw *pkgEncoder) stringIdx(s string) int { |
| if idx, ok := pw.stringsIdx[s]; ok { |
| assert(pw.elems[relocString][idx] == s) |
| return idx |
| } |
| |
| idx := len(pw.elems[relocString]) |
| pw.elems[relocString] = append(pw.elems[relocString], s) |
| pw.stringsIdx[s] = idx |
| return idx |
| } |
| |
| func (pw *pkgEncoder) newEncoder(k reloc, marker syncMarker) encoder { |
| e := pw.newEncoderRaw(k) |
| e.sync(marker) |
| return e |
| } |
| |
| func (pw *pkgEncoder) newEncoderRaw(k reloc) encoder { |
| idx := len(pw.elems[k]) |
| pw.elems[k] = append(pw.elems[k], "") // placeholder |
| |
| return encoder{ |
| p: pw, |
| k: k, |
| idx: idx, |
| } |
| } |
| |
| // Encoders |
| |
| type encoder struct { |
| p *pkgEncoder |
| |
| relocs []relocEnt |
| data bytes.Buffer |
| |
| encodingRelocHeader bool |
| |
| k reloc |
| idx int |
| } |
| |
| func (w *encoder) flush() int { |
| var sb bytes.Buffer // TODO(mdempsky): strings.Builder after #44505 is resolved |
| |
| // Backup the data so we write the relocations at the front. |
| var tmp bytes.Buffer |
| io.Copy(&tmp, &w.data) |
| |
| // TODO(mdempsky): Consider writing these out separately so they're |
| // easier to strip, along with function bodies, so that we can prune |
| // down to just the data that's relevant to go/types. |
| if w.encodingRelocHeader { |
| base.Fatalf("encodingRelocHeader already true; recursive flush?") |
| } |
| w.encodingRelocHeader = true |
| w.sync(syncRelocs) |
| w.len(len(w.relocs)) |
| for _, rent := range w.relocs { |
| w.sync(syncReloc) |
| w.len(int(rent.kind)) |
| w.len(rent.idx) |
| } |
| |
| io.Copy(&sb, &w.data) |
| io.Copy(&sb, &tmp) |
| w.p.elems[w.k][w.idx] = sb.String() |
| |
| return w.idx |
| } |
| |
| func (w *encoder) checkErr(err error) { |
| if err != nil { |
| base.Fatalf("unexpected error: %v", err) |
| } |
| } |
| |
| func (w *encoder) rawUvarint(x uint64) { |
| var buf [binary.MaxVarintLen64]byte |
| n := binary.PutUvarint(buf[:], x) |
| _, err := w.data.Write(buf[:n]) |
| w.checkErr(err) |
| } |
| |
| func (w *encoder) rawVarint(x int64) { |
| // Zig-zag encode. |
| ux := uint64(x) << 1 |
| if x < 0 { |
| ux = ^ux |
| } |
| |
| w.rawUvarint(ux) |
| } |
| |
| func (w *encoder) rawReloc(r reloc, idx int) int { |
| // TODO(mdempsky): Use map for lookup. |
| for i, rent := range w.relocs { |
| if rent.kind == r && rent.idx == idx { |
| return i |
| } |
| } |
| |
| i := len(w.relocs) |
| w.relocs = append(w.relocs, relocEnt{r, idx}) |
| return i |
| } |
| |
| func (w *encoder) sync(m syncMarker) { |
| if !enableSync { |
| return |
| } |
| |
| // Writing out stack frame string references requires working |
| // relocations, but writing out the relocations themselves involves |
| // sync markers. To prevent infinite recursion, we simply trim the |
| // stack frame for sync markers within the relocation header. |
| var frames []string |
| if !w.encodingRelocHeader && base.Debug.SyncFrames > 0 { |
| pcs := make([]uintptr, base.Debug.SyncFrames) |
| n := runtime.Callers(2, pcs) |
| frames = fmtFrames(pcs[:n]...) |
| } |
| |
| // TODO(mdempsky): Save space by writing out stack frames as a |
| // linked list so we can share common stack frames. |
| w.rawUvarint(uint64(m)) |
| w.rawUvarint(uint64(len(frames))) |
| for _, frame := range frames { |
| w.rawUvarint(uint64(w.rawReloc(relocString, w.p.stringIdx(frame)))) |
| } |
| } |
| |
| func (w *encoder) bool(b bool) bool { |
| w.sync(syncBool) |
| var x byte |
| if b { |
| x = 1 |
| } |
| err := w.data.WriteByte(x) |
| w.checkErr(err) |
| return b |
| } |
| |
| func (w *encoder) int64(x int64) { |
| w.sync(syncInt64) |
| w.rawVarint(x) |
| } |
| |
| func (w *encoder) uint64(x uint64) { |
| w.sync(syncUint64) |
| w.rawUvarint(x) |
| } |
| |
| func (w *encoder) len(x int) { assert(x >= 0); w.uint64(uint64(x)) } |
| func (w *encoder) int(x int) { w.int64(int64(x)) } |
| func (w *encoder) uint(x uint) { w.uint64(uint64(x)) } |
| |
| func (w *encoder) reloc(r reloc, idx int) { |
| w.sync(syncUseReloc) |
| w.len(w.rawReloc(r, idx)) |
| } |
| |
| func (w *encoder) code(c code) { |
| w.sync(c.marker()) |
| w.len(c.value()) |
| } |
| |
| func (w *encoder) string(s string) { |
| w.sync(syncString) |
| w.reloc(relocString, w.p.stringIdx(s)) |
| } |
| |
| func (w *encoder) strings(ss []string) { |
| w.len(len(ss)) |
| for _, s := range ss { |
| w.string(s) |
| } |
| } |
| |
| func (w *encoder) rawValue(val constant.Value) { |
| if w.bool(val.Kind() == constant.Complex) { |
| w.scalar(constant.Real(val)) |
| w.scalar(constant.Imag(val)) |
| } else { |
| w.scalar(val) |
| } |
| } |
| |
| func (w *encoder) scalar(val constant.Value) { |
| switch v := constant.Val(val).(type) { |
| default: |
| panic(fmt.Sprintf("unhandled %v (%v)", val, val.Kind())) |
| case bool: |
| w.code(valBool) |
| w.bool(v) |
| case string: |
| w.code(valString) |
| w.string(v) |
| case int64: |
| w.code(valInt64) |
| w.int64(v) |
| case *big.Int: |
| w.code(valBigInt) |
| w.bigInt(v) |
| case *big.Rat: |
| w.code(valBigRat) |
| w.bigInt(v.Num()) |
| w.bigInt(v.Denom()) |
| case *big.Float: |
| w.code(valBigFloat) |
| w.bigFloat(v) |
| } |
| } |
| |
| func (w *encoder) bigInt(v *big.Int) { |
| b := v.Bytes() |
| w.string(string(b)) // TODO: More efficient encoding. |
| w.bool(v.Sign() < 0) |
| } |
| |
| func (w *encoder) bigFloat(v *big.Float) { |
| b := v.Append(nil, 'p', -1) |
| w.string(string(b)) // TODO: More efficient encoding. |
| } |