//:build wasm package callback // The callback registry maps a host-visible integer id to a Moxie function // value. The host (JS) keeps the id and calls back through the Dispatch* // exports in dispatch.mx. // // Table is a self-mutating type: every registration mutates it through a // method, so the maps and the function values they hold live in the // registry's sovereign arena instead of the frame of whichever wasm export // happened to register the callback. Writes to a package global outside a // method - and the empty interface this registry used to store callbacks // through - are both forbidden in Moxie. type Table struct { nextID int32 once map[int32]bool f0 map[int32]func() fS map[int32]func(string) fB map[int32]func(bool) fI map[int32]func(int32) fIS map[int32]func(int32, string) fIIS map[int32]func(int32, int32, string) fSS map[int32]func(string, string) f6I map[int32]func(int32, int32, int32, int32, int32, int32) fII map[int32]func(int32, int32) fIII map[int32]func(int32, int32, int32) fBytes map[int32]func([]byte) } var table Table // add allocates the next id and records whether it fires once. func (t *Table) add(once bool) (id int32) { if t.once == nil { t.once = map[int32]bool{} } t.nextID++ id = t.nextID if once { t.once[id] = true } return } // Release drops every entry for id. func (t *Table) Release(id int32) { delete(t.f0, id) delete(t.fS, id) delete(t.fB, id) delete(t.fI, id) delete(t.fIS, id) delete(t.fIIS, id) delete(t.fSS, id) delete(t.f6I, id) delete(t.fII, id) delete(t.fIII, id) delete(t.fBytes, id) delete(t.once, id) } func (t *Table) Register0(fn func()) (n int32) { n = t.add(false) if t.f0 == nil { t.f0 = map[int32]func(){} } t.f0[n] = fn return } func (t *Table) Register0Once(fn func()) (n int32) { n = t.add(true) if t.f0 == nil { t.f0 = map[int32]func(){} } t.f0[n] = fn return } func (t *Table) RegisterS(fn func(string)) (n int32) { n = t.add(false) if t.fS == nil { t.fS = map[int32]func(string){} } t.fS[n] = fn return } func (t *Table) RegisterSOnce(fn func(string)) (n int32) { n = t.add(true) if t.fS == nil { t.fS = map[int32]func(string){} } t.fS[n] = fn return } func (t *Table) RegisterB(fn func(bool)) (n int32) { n = t.add(false) if t.fB == nil { t.fB = map[int32]func(bool){} } t.fB[n] = fn return } func (t *Table) RegisterBOnce(fn func(bool)) (n int32) { n = t.add(true) if t.fB == nil { t.fB = map[int32]func(bool){} } t.fB[n] = fn return } func (t *Table) RegisterI(fn func(int32)) (n int32) { n = t.add(false) if t.fI == nil { t.fI = map[int32]func(int32){} } t.fI[n] = fn return } func (t *Table) RegisterIOnce(fn func(int32)) (n int32) { n = t.add(true) if t.fI == nil { t.fI = map[int32]func(int32){} } t.fI[n] = fn return } func (t *Table) RegisterIS(fn func(int32, string)) (n int32) { n = t.add(false) if t.fIS == nil { t.fIS = map[int32]func(int32, string){} } t.fIS[n] = fn return } func (t *Table) RegisterISOnce(fn func(int32, string)) (n int32) { n = t.add(true) if t.fIS == nil { t.fIS = map[int32]func(int32, string){} } t.fIS[n] = fn return } func (t *Table) RegisterIIS(fn func(int32, int32, string)) (n int32) { n = t.add(false) if t.fIIS == nil { t.fIIS = map[int32]func(int32, int32, string){} } t.fIIS[n] = fn return } func (t *Table) RegisterIISOnce(fn func(int32, int32, string)) (n int32) { n = t.add(true) if t.fIIS == nil { t.fIIS = map[int32]func(int32, int32, string){} } t.fIIS[n] = fn return } func (t *Table) RegisterSS(fn func(string, string)) (n int32) { n = t.add(false) if t.fSS == nil { t.fSS = map[int32]func(string, string){} } t.fSS[n] = fn return } func (t *Table) RegisterSSOnce(fn func(string, string)) (n int32) { n = t.add(true) if t.fSS == nil { t.fSS = map[int32]func(string, string){} } t.fSS[n] = fn return } func (t *Table) Register6I(fn func(int32, int32, int32, int32, int32, int32)) (n int32) { n = t.add(false) if t.f6I == nil { t.f6I = map[int32]func(int32, int32, int32, int32, int32, int32){} } t.f6I[n] = fn return } func (t *Table) Register6IOnce(fn func(int32, int32, int32, int32, int32, int32)) (n int32) { n = t.add(true) if t.f6I == nil { t.f6I = map[int32]func(int32, int32, int32, int32, int32, int32){} } t.f6I[n] = fn return } func (t *Table) RegisterII(fn func(int32, int32)) (n int32) { n = t.add(false) if t.fII == nil { t.fII = map[int32]func(int32, int32){} } t.fII[n] = fn return } func (t *Table) RegisterIIOnce(fn func(int32, int32)) (n int32) { n = t.add(true) if t.fII == nil { t.fII = map[int32]func(int32, int32){} } t.fII[n] = fn return } func (t *Table) RegisterIII(fn func(int32, int32, int32)) (n int32) { n = t.add(false) if t.fIII == nil { t.fIII = map[int32]func(int32, int32, int32){} } t.fIII[n] = fn return } func (t *Table) RegisterIIIOnce(fn func(int32, int32, int32)) (n int32) { n = t.add(true) if t.fIII == nil { t.fIII = map[int32]func(int32, int32, int32){} } t.fIII[n] = fn return } func (t *Table) RegisterBytes(fn func([]byte)) (n int32) { n = t.add(false) if t.fBytes == nil { t.fBytes = map[int32]func([]byte){} } t.fBytes[n] = fn return } func (t *Table) RegisterBytesOnce(fn func([]byte)) (n int32) { n = t.add(true) if t.fBytes == nil { t.fBytes = map[int32]func([]byte){} } t.fBytes[n] = fn return } // --- typed lookups: one per signature, so the dispatcher calls the callback // directly instead of asserting an interface{} back to a function type. func (t *Table) Lookup0(id int32) (fn func(), ok bool) { fn, ok = t.f0[id] if ok && t.once[id] { t.Release(id) } return } func (t *Table) LookupS(id int32) (fn func(string), ok bool) { fn, ok = t.fS[id] if ok && t.once[id] { t.Release(id) } return } func (t *Table) LookupB(id int32) (fn func(bool), ok bool) { fn, ok = t.fB[id] if ok && t.once[id] { t.Release(id) } return } func (t *Table) LookupI(id int32) (fn func(int32), ok bool) { fn, ok = t.fI[id] if ok && t.once[id] { t.Release(id) } return } func (t *Table) LookupIS(id int32) (fn func(int32, string), ok bool) { fn, ok = t.fIS[id] if ok && t.once[id] { t.Release(id) } return } func (t *Table) LookupIIS(id int32) (fn func(int32, int32, string), ok bool) { fn, ok = t.fIIS[id] if ok && t.once[id] { t.Release(id) } return } func (t *Table) LookupSS(id int32) (fn func(string, string), ok bool) { fn, ok = t.fSS[id] if ok && t.once[id] { t.Release(id) } return } func (t *Table) Lookup6I(id int32) (fn func(int32, int32, int32, int32, int32, int32), ok bool) { fn, ok = t.f6I[id] if ok && t.once[id] { t.Release(id) } return } func (t *Table) LookupII(id int32) (fn func(int32, int32), ok bool) { fn, ok = t.fII[id] if ok && t.once[id] { t.Release(id) } return } func (t *Table) LookupIII(id int32) (fn func(int32, int32, int32), ok bool) { fn, ok = t.fIII[id] if ok && t.once[id] { t.Release(id) } return } func (t *Table) LookupBytes(id int32) (fn func([]byte), ok bool) { fn, ok = t.fBytes[id] if ok && t.once[id] { t.Release(id) } return } // --- package-level entry points used by the jsbridge packages. func Register0(fn func()) (n int32) { return table.Register0(fn) } func Register0Once(fn func()) (n int32) { return table.Register0Once(fn) } func RegisterS(fn func(string)) (n int32) { return table.RegisterS(fn) } func RegisterSOnce(fn func(string)) (n int32) { return table.RegisterSOnce(fn) } func RegisterB(fn func(bool)) (n int32) { return table.RegisterB(fn) } func RegisterBOnce(fn func(bool)) (n int32) { return table.RegisterBOnce(fn) } func RegisterI(fn func(int32)) (n int32) { return table.RegisterI(fn) } func RegisterIOnce(fn func(int32)) (n int32) { return table.RegisterIOnce(fn) } func RegisterIS(fn func(int32, string)) (n int32) { return table.RegisterIS(fn) } func RegisterISOnce(fn func(int32, string)) (n int32) { return table.RegisterISOnce(fn) } func RegisterIIS(fn func(int32, int32, string)) (n int32) { return table.RegisterIIS(fn) } func RegisterIISOnce(fn func(int32, int32, string)) (n int32) { return table.RegisterIISOnce(fn) } func RegisterSS(fn func(string, string)) (n int32) { return table.RegisterSS(fn) } func RegisterSSOnce(fn func(string, string)) (n int32) { return table.RegisterSSOnce(fn) } func Register6I(fn func(int32, int32, int32, int32, int32, int32)) (n int32) { return table.Register6I(fn) } func Register6IOnce(fn func(int32, int32, int32, int32, int32, int32)) (n int32) { return table.Register6IOnce(fn) } func RegisterII(fn func(int32, int32)) (n int32) { return table.RegisterII(fn) } func RegisterIIOnce(fn func(int32, int32)) (n int32) { return table.RegisterIIOnce(fn) } func RegisterIII(fn func(int32, int32, int32)) (n int32) { return table.RegisterIII(fn) } func RegisterIIIOnce(fn func(int32, int32, int32)) (n int32) { return table.RegisterIIIOnce(fn) } func RegisterBytes(fn func([]byte)) (n int32) { return table.RegisterBytes(fn) } func RegisterBytesOnce(fn func([]byte)) (n int32) { return table.RegisterBytesOnce(fn) } // Release drops a registration (the host releases ids it no longer needs). func Release(id int32) { table.Release(id) }