330 lines
7.5 KiB
Go
330 lines
7.5 KiB
Go
package abi
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"errors"
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// errFrozen is returned by every Add* once Freeze has sealed the table.
|
|
var errFrozen = errors.New("abi: table is frozen")
|
|
|
|
const MaxStructSlots = 32
|
|
|
|
type Table struct {
|
|
modules map[string]bool
|
|
funcs map[string]Signature
|
|
methods map[string]Signature
|
|
cmethods map[string]Signature
|
|
consts map[string]Const
|
|
enums map[string]*EnumDef
|
|
structs map[string]*StructDef
|
|
events []EventDef
|
|
eventIdx map[string]int
|
|
comps []*Component
|
|
hash [32]byte
|
|
frozen bool
|
|
}
|
|
|
|
func NewTable() *Table {
|
|
return &Table{
|
|
modules: map[string]bool{},
|
|
funcs: map[string]Signature{},
|
|
methods: map[string]Signature{},
|
|
cmethods: map[string]Signature{},
|
|
consts: map[string]Const{},
|
|
enums: map[string]*EnumDef{},
|
|
structs: map[string]*StructDef{},
|
|
eventIdx: map[string]int{},
|
|
}
|
|
}
|
|
|
|
func key(a, b string) string {
|
|
return a + "\x00" + b
|
|
}
|
|
|
|
func (t *Table) AddModule(path string) error {
|
|
if t.frozen {
|
|
return errFrozen
|
|
}
|
|
t.modules[path] = true
|
|
return nil
|
|
}
|
|
|
|
func (t *Table) AddFunc(module, name string, sig Signature) error {
|
|
if t.frozen {
|
|
return errFrozen
|
|
}
|
|
if err := sig.Validate(); err != nil {
|
|
return fmt.Errorf("func %s.%s: %w", module, name, err)
|
|
}
|
|
k := key(module, name)
|
|
if _, dup := t.funcs[k]; dup {
|
|
return fmt.Errorf("func %s.%s already registered", module, name)
|
|
}
|
|
t.modules[module] = true
|
|
t.funcs[k] = sig
|
|
return nil
|
|
}
|
|
|
|
func (t *Table) AddMethod(recv Kind, name string, sig Signature) error {
|
|
if t.frozen {
|
|
return errFrozen
|
|
}
|
|
if err := sig.Validate(); err != nil {
|
|
return fmt.Errorf("method %s.%s: %w", recv, name, err)
|
|
}
|
|
k := key(recv.String(), name)
|
|
if _, dup := t.methods[k]; dup {
|
|
return fmt.Errorf("method %s.%s already registered", recv, name)
|
|
}
|
|
t.methods[k] = sig
|
|
return nil
|
|
}
|
|
|
|
func (t *Table) AddComponentMethod(comp, name string, sig Signature) error {
|
|
if t.frozen {
|
|
return errFrozen
|
|
}
|
|
if err := sig.Validate(); err != nil {
|
|
return fmt.Errorf("method %s.%s: %w", comp, name, err)
|
|
}
|
|
k := key(comp, name)
|
|
if _, dup := t.cmethods[k]; dup {
|
|
return fmt.Errorf("method %s.%s already registered", comp, name)
|
|
}
|
|
t.cmethods[k] = sig
|
|
return nil
|
|
}
|
|
|
|
func (t *Table) AddConst(module, name string, c Const) error {
|
|
if t.frozen {
|
|
return errFrozen
|
|
}
|
|
k := key(module, name)
|
|
if _, dup := t.consts[k]; dup {
|
|
return fmt.Errorf("const %s.%s already registered", module, name)
|
|
}
|
|
t.modules[module] = true
|
|
t.consts[k] = c
|
|
return nil
|
|
}
|
|
|
|
func (t *Table) AddEnum(e *EnumDef) error {
|
|
if t.frozen {
|
|
return errFrozen
|
|
}
|
|
k := key(e.Module, e.Name)
|
|
if _, dup := t.enums[k]; dup {
|
|
return fmt.Errorf("enum %s already registered", e.Qualified())
|
|
}
|
|
t.modules[e.Module] = true
|
|
t.enums[k] = e
|
|
return nil
|
|
}
|
|
|
|
func (t *Table) AddStruct(s *StructDef) error {
|
|
if t.frozen {
|
|
return errFrozen
|
|
}
|
|
q := s.Qualified()
|
|
if _, dup := t.structs[q]; dup {
|
|
return fmt.Errorf("struct %s already registered", q)
|
|
}
|
|
if s.Slots <= 0 || s.Slots > MaxStructSlots {
|
|
return fmt.Errorf("struct %s has %d value slots, the limit is %d", q, s.Slots, MaxStructSlots)
|
|
}
|
|
for _, f := range s.Fields {
|
|
if !Plain(f.Type) {
|
|
return fmt.Errorf("struct %s field %s has type %s, which cannot live in a host struct", q, f.Name, f.Type)
|
|
}
|
|
}
|
|
t.modules[s.Module] = true
|
|
t.structs[q] = s
|
|
return nil
|
|
}
|
|
|
|
func (t *Table) AddEvent(e EventDef) error {
|
|
if t.frozen {
|
|
return errFrozen
|
|
}
|
|
if !Exported(e.Name) {
|
|
return fmt.Errorf("event %q must have an exported name", e.Name)
|
|
}
|
|
if _, dup := t.eventIdx[e.Name]; dup {
|
|
return fmt.Errorf("event %s already registered", e.Name)
|
|
}
|
|
for i, p := range e.Params {
|
|
if !Plain(p) {
|
|
return fmt.Errorf("event %s param %d: %s cannot be an event parameter", e.Name, i, p)
|
|
}
|
|
}
|
|
t.eventIdx[e.Name] = len(t.events)
|
|
t.events = append(t.events, EventDef{Name: e.Name, Params: append([]TypeRef(nil), e.Params...)})
|
|
return nil
|
|
}
|
|
|
|
func (t *Table) AddComponent(c *Component) error {
|
|
if t.frozen {
|
|
return errFrozen
|
|
}
|
|
if c.Short == "" {
|
|
c.Short = c.Name[strings.LastIndexByte(c.Name, '.')+1:]
|
|
}
|
|
for _, e := range t.comps {
|
|
if e.Name == c.Name {
|
|
return fmt.Errorf("component %s already registered", c.Name)
|
|
}
|
|
}
|
|
for i := range c.Fields {
|
|
c.Fields[i].Index = i
|
|
}
|
|
c.Index = len(t.comps)
|
|
t.comps = append(t.comps, c)
|
|
return nil
|
|
}
|
|
|
|
func (t *Table) Module(path string) bool {
|
|
return t.modules[path]
|
|
}
|
|
|
|
func (t *Table) Func(module, name string) (Signature, bool) {
|
|
s, ok := t.funcs[key(module, name)]
|
|
return s, ok
|
|
}
|
|
|
|
func (t *Table) Method(recv Kind, name string) (Signature, bool) {
|
|
s, ok := t.methods[key(recv.String(), name)]
|
|
return s, ok
|
|
}
|
|
|
|
func (t *Table) ComponentMethod(comp, name string) (Signature, bool) {
|
|
s, ok := t.cmethods[key(comp, name)]
|
|
return s, ok
|
|
}
|
|
|
|
func (t *Table) Const(module, name string) (Const, bool) {
|
|
c, ok := t.consts[key(module, name)]
|
|
return c, ok
|
|
}
|
|
|
|
func (t *Table) Enum(module, name string) (*EnumDef, bool) {
|
|
e, ok := t.enums[key(module, name)]
|
|
return e, ok
|
|
}
|
|
|
|
func (t *Table) Struct(module, name string) (*StructDef, bool) {
|
|
s, ok := t.structs[module+"."+name]
|
|
return s, ok
|
|
}
|
|
|
|
func (t *Table) Event(name string) (Signature, bool) {
|
|
i, ok := t.eventIdx[name]
|
|
if !ok {
|
|
return Signature{}, false
|
|
}
|
|
return t.events[i].Sig(), true
|
|
}
|
|
|
|
func (t *Table) EventIndex(name string) (int, bool) {
|
|
i, ok := t.eventIdx[name]
|
|
return i, ok
|
|
}
|
|
|
|
func (t *Table) Slots(r TypeRef) int {
|
|
if r.Kind == Struct {
|
|
if s, ok := t.structs[r.Name]; ok {
|
|
return s.Slots
|
|
}
|
|
}
|
|
return r.Slots()
|
|
}
|
|
|
|
func (t *Table) Components(name string) []*Component {
|
|
for _, c := range t.comps {
|
|
if c.Name == name {
|
|
return []*Component{c}
|
|
}
|
|
}
|
|
var out []*Component
|
|
for _, c := range t.comps {
|
|
if c.Short == name {
|
|
out = append(out, c)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (t *Table) AllComponents() []*Component {
|
|
return t.comps
|
|
}
|
|
|
|
func (t *Table) Hash() [32]byte {
|
|
if !t.frozen {
|
|
panic("abi: Hash called on an unfrozen table; call Freeze first")
|
|
}
|
|
return t.hash
|
|
}
|
|
|
|
// Freeze seals the table and computes its hash; later Add* calls return errFrozen.
|
|
func (t *Table) Freeze() {
|
|
if t.frozen {
|
|
return
|
|
}
|
|
var lines []string
|
|
for p := range t.modules {
|
|
lines = append(lines, "p|"+p)
|
|
}
|
|
for k, s := range t.funcs {
|
|
lines = append(lines, "f|"+k+"|"+s.String())
|
|
}
|
|
for k, s := range t.methods {
|
|
lines = append(lines, "m|"+k+"|"+s.String())
|
|
}
|
|
for k, s := range t.cmethods {
|
|
lines = append(lines, "q|"+k+"|"+s.String())
|
|
}
|
|
for k, c := range t.consts {
|
|
lines = append(lines, "c|"+k+"|"+c.Type.String()+"|"+strconv.FormatInt(c.Int, 10)+"|"+
|
|
strconv.FormatFloat(c.Float, 'g', -1, 64)+"|"+strconv.FormatBool(c.Bool)+"|"+c.Str)
|
|
}
|
|
for k, e := range t.enums {
|
|
var b strings.Builder
|
|
b.WriteString("e|" + k)
|
|
for _, m := range e.Members {
|
|
b.WriteString("|" + m.Name + "=" + strconv.FormatInt(m.Value, 10))
|
|
}
|
|
lines = append(lines, b.String())
|
|
}
|
|
for k, s := range t.structs {
|
|
var b strings.Builder
|
|
b.WriteString("t|" + k + "|" + strconv.Itoa(s.Slots))
|
|
for _, f := range s.Fields {
|
|
b.WriteString("|" + f.Name + ":" + f.Type.String())
|
|
}
|
|
lines = append(lines, b.String())
|
|
}
|
|
for i, e := range t.events {
|
|
lines = append(lines, "v|"+strconv.Itoa(i)+"|"+e.Name+"|"+e.Sig().String())
|
|
}
|
|
for _, c := range t.comps {
|
|
var b strings.Builder
|
|
b.WriteString("k|" + c.Name)
|
|
for _, f := range c.Fields {
|
|
b.WriteString("|" + f.Name + ":" + f.Type.String() + ":" + strconv.FormatBool(f.ReadOnly))
|
|
}
|
|
lines = append(lines, b.String())
|
|
}
|
|
sort.Strings(lines)
|
|
h := sha256.New()
|
|
for _, l := range lines {
|
|
h.Write([]byte(l))
|
|
h.Write([]byte{'\n'})
|
|
}
|
|
copy(t.hash[:], h.Sum(nil))
|
|
t.frozen = true
|
|
}
|