484 lines
11 KiB
Go
484 lines
11 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
|
|
hmethods map[string]Signature
|
|
hsignals map[string][]SignalDef
|
|
handles map[string]HandleDef
|
|
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{},
|
|
hmethods: map[string]Signature{},
|
|
hsignals: map[string][]SignalDef{},
|
|
handles: map[string]HandleDef{},
|
|
consts: map[string]Const{},
|
|
enums: map[string]*EnumDef{},
|
|
structs: map[string]*StructDef{},
|
|
eventIdx: map[string]int{},
|
|
}
|
|
}
|
|
|
|
func (t *Table) AddHandle(h HandleDef) error {
|
|
if t.frozen {
|
|
return errFrozen
|
|
}
|
|
i := strings.LastIndexByte(h.Name, '.')
|
|
if i <= 0 || i == len(h.Name)-1 || !Exported(h.Name[i+1:]) {
|
|
return fmt.Errorf("handle type %q needs the form module.Name", h.Name)
|
|
}
|
|
if _, dup := t.handles[h.Name]; dup {
|
|
return fmt.Errorf("handle type %s already registered", h.Name)
|
|
}
|
|
t.modules[h.Name[:i]] = true
|
|
t.handles[h.Name] = h
|
|
return nil
|
|
}
|
|
|
|
func (t *Table) hasHandleSignal(handle, name string) bool {
|
|
for _, s := range t.hsignals[handle] {
|
|
if s.Name == name {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t *Table) AddHandleMethod(handle, name string, sig Signature) error {
|
|
if t.frozen {
|
|
return errFrozen
|
|
}
|
|
if err := sig.Validate(); err != nil {
|
|
return fmt.Errorf("method %s.%s: %w", handle, name, err)
|
|
}
|
|
k := key(handle, name)
|
|
if _, dup := t.hmethods[k]; dup || t.hasHandleSignal(handle, name) {
|
|
return fmt.Errorf("method %s.%s already registered", handle, name)
|
|
}
|
|
t.hmethods[k] = sig
|
|
return nil
|
|
}
|
|
|
|
func (t *Table) AddHandleSignal(handle string, s SignalDef) error {
|
|
if t.frozen {
|
|
return errFrozen
|
|
}
|
|
if !Exported(s.Name) {
|
|
return fmt.Errorf("signal %s.%s needs an exported name", handle, s.Name)
|
|
}
|
|
if _, dup := t.hmethods[key(handle, s.Name)]; dup || t.hasHandleSignal(handle, s.Name) {
|
|
return fmt.Errorf("signal %s.%s already registered", handle, s.Name)
|
|
}
|
|
for i, p := range s.Params {
|
|
if !Plain(p) {
|
|
return fmt.Errorf("signal %s.%s param %d: %s cannot be a signal parameter", handle, s.Name, i, p)
|
|
}
|
|
}
|
|
t.hsignals[handle] = append(t.hsignals[handle], SignalDef{Name: s.Name, Params: append([]TypeRef(nil), s.Params...)})
|
|
return nil
|
|
}
|
|
|
|
func (t *Table) HandleSignals(handle string) []SignalDef {
|
|
return t.hsignals[handle]
|
|
}
|
|
|
|
func (t *Table) HandleSignal(handle, name string) (int, SignalDef, bool) {
|
|
for i, s := range t.hsignals[handle] {
|
|
if s.Name == name {
|
|
return i, s, true
|
|
}
|
|
}
|
|
return -1, SignalDef{}, false
|
|
}
|
|
|
|
type HandleSignalEntry struct {
|
|
Handle string
|
|
Index int
|
|
Def SignalDef
|
|
}
|
|
|
|
func (t *Table) HandleSignalList() []HandleSignalEntry {
|
|
var out []HandleSignalEntry
|
|
for h, list := range t.hsignals {
|
|
for i, s := range list {
|
|
out = append(out, HandleSignalEntry{Handle: h, Index: i, Def: s})
|
|
}
|
|
}
|
|
sort.Slice(out, func(i, j int) bool {
|
|
if out[i].Handle != out[j].Handle {
|
|
return out[i].Handle < out[j].Handle
|
|
}
|
|
return out[i].Index < out[j].Index
|
|
})
|
|
return out
|
|
}
|
|
|
|
func (t *Table) Handle(name string) (HandleDef, bool) {
|
|
h, ok := t.handles[name]
|
|
return h, ok
|
|
}
|
|
|
|
func (t *Table) HandleMethod(handle, name string) (Signature, bool) {
|
|
s, ok := t.hmethods[key(handle, name)]
|
|
return s, ok
|
|
}
|
|
|
|
type HandleMethodEntry struct {
|
|
Handle string
|
|
Name string
|
|
Sig Signature
|
|
}
|
|
|
|
func (t *Table) HandleList() []HandleDef {
|
|
out := make([]HandleDef, 0, len(t.handles))
|
|
for _, h := range t.handles {
|
|
out = append(out, h)
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
|
|
return out
|
|
}
|
|
|
|
func (t *Table) HandleMethodList() []HandleMethodEntry {
|
|
out := make([]HandleMethodEntry, 0, len(t.hmethods))
|
|
for k, s := range t.hmethods {
|
|
h, n, _ := strings.Cut(k, "\x00")
|
|
out = append(out, HandleMethodEntry{Handle: h, Name: n, Sig: s})
|
|
}
|
|
sort.Slice(out, func(i, j int) bool {
|
|
if out[i].Handle != out[j].Handle {
|
|
return out[i].Handle < out[j].Handle
|
|
}
|
|
return out[i].Name < out[j].Name
|
|
})
|
|
return out
|
|
}
|
|
|
|
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 !EventParam(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, s := range t.hmethods {
|
|
lines = append(lines, "y|"+k+"|"+s.String())
|
|
}
|
|
for h, list := range t.hsignals {
|
|
for i, s := range list {
|
|
lines = append(lines, "z|"+h+"|"+strconv.Itoa(i)+"|"+s.Name+"|"+s.Sig().String())
|
|
}
|
|
}
|
|
for k, h := range t.handles {
|
|
lines = append(lines, "h|"+k+"|"+strconv.FormatBool(h.Await))
|
|
}
|
|
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))
|
|
}
|
|
for _, s := range c.Signals {
|
|
b.WriteString("|!" + s.Name + s.Sig().String())
|
|
}
|
|
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
|
|
}
|