| // 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. | |
| } |