forked from cerc-io/ipld-eth-server
Add Vat init transformer
This commit is contained in:
+272
@@ -0,0 +1,272 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type sizeState uint8
|
||||
|
||||
const (
|
||||
// need to write "s = ..."
|
||||
assign sizeState = iota
|
||||
|
||||
// need to write "s += ..."
|
||||
add
|
||||
|
||||
// can just append "+ ..."
|
||||
expr
|
||||
)
|
||||
|
||||
func sizes(w io.Writer) *sizeGen {
|
||||
return &sizeGen{
|
||||
p: printer{w: w},
|
||||
state: assign,
|
||||
}
|
||||
}
|
||||
|
||||
type sizeGen struct {
|
||||
passes
|
||||
p printer
|
||||
state sizeState
|
||||
}
|
||||
|
||||
func (s *sizeGen) Method() Method { return Size }
|
||||
|
||||
func (s *sizeGen) Apply(dirs []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func builtinSize(typ string) string {
|
||||
return "msgp." + typ + "Size"
|
||||
}
|
||||
|
||||
// this lets us chain together addition
|
||||
// operations where possible
|
||||
func (s *sizeGen) addConstant(sz string) {
|
||||
if !s.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
switch s.state {
|
||||
case assign:
|
||||
s.p.print("\ns = " + sz)
|
||||
s.state = expr
|
||||
return
|
||||
case add:
|
||||
s.p.print("\ns += " + sz)
|
||||
s.state = expr
|
||||
return
|
||||
case expr:
|
||||
s.p.print(" + " + sz)
|
||||
return
|
||||
}
|
||||
|
||||
panic("unknown size state")
|
||||
}
|
||||
|
||||
func (s *sizeGen) Execute(p Elem) error {
|
||||
if !s.p.ok() {
|
||||
return s.p.err
|
||||
}
|
||||
p = s.applyall(p)
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
if !IsPrintable(p) {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.p.comment("Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message")
|
||||
|
||||
s.p.printf("\nfunc (%s %s) Msgsize() (s int) {", p.Varname(), imutMethodReceiver(p))
|
||||
s.state = assign
|
||||
next(s, p)
|
||||
s.p.nakedReturn()
|
||||
return s.p.err
|
||||
}
|
||||
|
||||
func (s *sizeGen) gStruct(st *Struct) {
|
||||
if !s.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
nfields := uint32(len(st.Fields))
|
||||
|
||||
if st.AsTuple {
|
||||
data := msgp.AppendArrayHeader(nil, nfields)
|
||||
s.addConstant(strconv.Itoa(len(data)))
|
||||
for i := range st.Fields {
|
||||
if !s.p.ok() {
|
||||
return
|
||||
}
|
||||
next(s, st.Fields[i].FieldElem)
|
||||
}
|
||||
} else {
|
||||
data := msgp.AppendMapHeader(nil, nfields)
|
||||
s.addConstant(strconv.Itoa(len(data)))
|
||||
for i := range st.Fields {
|
||||
data = data[:0]
|
||||
data = msgp.AppendString(data, st.Fields[i].FieldTag)
|
||||
s.addConstant(strconv.Itoa(len(data)))
|
||||
next(s, st.Fields[i].FieldElem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sizeGen) gPtr(p *Ptr) {
|
||||
s.state = add // inner must use add
|
||||
s.p.printf("\nif %s == nil {\ns += msgp.NilSize\n} else {", p.Varname())
|
||||
next(s, p.Value)
|
||||
s.state = add // closing block; reset to add
|
||||
s.p.closeblock()
|
||||
}
|
||||
|
||||
func (s *sizeGen) gSlice(sl *Slice) {
|
||||
if !s.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
s.addConstant(builtinSize(arrayHeader))
|
||||
|
||||
// if the slice's element is a fixed size
|
||||
// (e.g. float64, [32]int, etc.), then
|
||||
// print the length times the element size directly
|
||||
if str, ok := fixedsizeExpr(sl.Els); ok {
|
||||
s.addConstant(fmt.Sprintf("(%s * (%s))", lenExpr(sl), str))
|
||||
return
|
||||
}
|
||||
|
||||
// add inside the range block, and immediately after
|
||||
s.state = add
|
||||
s.p.rangeBlock(sl.Index, sl.Varname(), s, sl.Els)
|
||||
s.state = add
|
||||
}
|
||||
|
||||
func (s *sizeGen) gArray(a *Array) {
|
||||
if !s.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
s.addConstant(builtinSize(arrayHeader))
|
||||
|
||||
// if the array's children are a fixed
|
||||
// size, we can compile an expression
|
||||
// that always represents the array's wire size
|
||||
if str, ok := fixedsizeExpr(a); ok {
|
||||
s.addConstant(str)
|
||||
return
|
||||
}
|
||||
|
||||
s.state = add
|
||||
s.p.rangeBlock(a.Index, a.Varname(), s, a.Els)
|
||||
s.state = add
|
||||
}
|
||||
|
||||
func (s *sizeGen) gMap(m *Map) {
|
||||
s.addConstant(builtinSize(mapHeader))
|
||||
vn := m.Varname()
|
||||
s.p.printf("\nif %s != nil {", vn)
|
||||
s.p.printf("\nfor %s, %s := range %s {", m.Keyidx, m.Validx, vn)
|
||||
s.p.printf("\n_ = %s", m.Validx) // we may not use the value
|
||||
s.p.printf("\ns += msgp.StringPrefixSize + len(%s)", m.Keyidx)
|
||||
s.state = expr
|
||||
next(s, m.Value)
|
||||
s.p.closeblock()
|
||||
s.p.closeblock()
|
||||
s.state = add
|
||||
}
|
||||
|
||||
func (s *sizeGen) gBase(b *BaseElem) {
|
||||
if !s.p.ok() {
|
||||
return
|
||||
}
|
||||
s.addConstant(basesizeExpr(b))
|
||||
}
|
||||
|
||||
// returns "len(slice)"
|
||||
func lenExpr(sl *Slice) string {
|
||||
return "len(" + sl.Varname() + ")"
|
||||
}
|
||||
|
||||
// is a given primitive always the same (max)
|
||||
// size on the wire?
|
||||
func fixedSize(p Primitive) bool {
|
||||
switch p {
|
||||
case Intf, Ext, IDENT, Bytes, String:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// strip reference from string
|
||||
func stripRef(s string) string {
|
||||
if s[0] == '&' {
|
||||
return s[1:]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// return a fixed-size expression, if possible.
|
||||
// only possible for *BaseElem and *Array.
|
||||
// returns (expr, ok)
|
||||
func fixedsizeExpr(e Elem) (string, bool) {
|
||||
switch e := e.(type) {
|
||||
case *Array:
|
||||
if str, ok := fixedsizeExpr(e.Els); ok {
|
||||
return fmt.Sprintf("(%s * (%s))", e.Size, str), true
|
||||
}
|
||||
case *BaseElem:
|
||||
if fixedSize(e.Value) {
|
||||
return builtinSize(e.BaseName()), true
|
||||
}
|
||||
case *Struct:
|
||||
var str string
|
||||
for _, f := range e.Fields {
|
||||
if fs, ok := fixedsizeExpr(f.FieldElem); ok {
|
||||
if str == "" {
|
||||
str = fs
|
||||
} else {
|
||||
str += "+" + fs
|
||||
}
|
||||
} else {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
var hdrlen int
|
||||
mhdr := msgp.AppendMapHeader(nil, uint32(len(e.Fields)))
|
||||
hdrlen += len(mhdr)
|
||||
var strbody []byte
|
||||
for _, f := range e.Fields {
|
||||
strbody = msgp.AppendString(strbody[:0], f.FieldTag)
|
||||
hdrlen += len(strbody)
|
||||
}
|
||||
return fmt.Sprintf("%d + %s", hdrlen, str), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// print size expression of a variable name
|
||||
func basesizeExpr(b *BaseElem) string {
|
||||
vname := b.Varname()
|
||||
if b.Convert {
|
||||
vname = tobaseConvert(b)
|
||||
}
|
||||
switch b.Value {
|
||||
case Ext:
|
||||
return "msgp.ExtensionPrefixSize + " + stripRef(vname) + ".Len()"
|
||||
case Intf:
|
||||
return "msgp.GuessSize(" + vname + ")"
|
||||
case IDENT:
|
||||
return vname + ".Msgsize()"
|
||||
case Bytes:
|
||||
return "msgp.BytesPrefixSize + len(" + vname + ")"
|
||||
case String:
|
||||
return "msgp.StringPrefixSize + len(" + vname + ")"
|
||||
default:
|
||||
return builtinSize(b.BaseName())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user