diff --git a/table.go b/table.go index 7a92c89ce205..2f6421efe47c 100644 --- a/table.go +++ b/table.go @@ -1,8 +1,11 @@ package wasmtime -// #include +// #include import "C" -import "runtime" +import ( + "errors" + "runtime" +) type Table struct { _ptr *C.wasm_table_t @@ -10,6 +13,26 @@ type Table struct { freelist *freeList } +// Creates a new `Table` in the given `Store` with the specified `ty`. +// +// The `ty` must be a `funcref` table and `init` is the initial value for all +// table slots, and is allowed to be `nil`. +func NewTable(store *Store, ty *TableType, init *Func) (*Table, error) { + var init_ptr *C.wasm_func_t + if init != nil { + init_ptr = init.ptr() + } + var ptr *C.wasm_table_t + err := C.wasmtime_funcref_table_new(store.ptr(), ty.ptr(), init_ptr, &ptr) + runtime.KeepAlive(store) + runtime.KeepAlive(ty) + runtime.KeepAlive(init) + if err != nil { + return nil, mkError(err) + } + return mkTable(ptr, store.freelist, nil), nil +} + func mkTable(ptr *C.wasm_table_t, freelist *freeList, owner interface{}) *Table { f := &Table{_ptr: ptr, _owner: owner, freelist: freelist} if owner == nil { @@ -35,12 +58,74 @@ func (t *Table) owner() interface{} { return t } +// Returns the size of this table in units of elements. func (t *Table) Size() uint32 { ret := C.wasm_table_size(t.ptr()) runtime.KeepAlive(t) return uint32(ret) } +// Grows this funcref table by the number of units specified, using the +// specified initializer value for new slots. +// +// Note that `init` is allowed to be `nil`. +// +// Returns an error if the table failed to grow, or the previous size of the +// table if growth was successful. +func (t *Table) Grow(delta uint32, init *Func) (uint32, error) { + var init_ptr *C.wasm_func_t + if init != nil { + init_ptr = init.ptr() + } + var prev C.uint32_t + err := C.wasmtime_funcref_table_grow(t.ptr(), C.uint32_t(delta), init_ptr, &prev) + runtime.KeepAlive(t) + runtime.KeepAlive(init) + if err == nil { + return uint32(prev), nil + } else { + return 0, mkError(err) + } +} + +// Gets an item from this table from the specified index. +// +// Returns an error if the index is out of bounds, or returns a function (which +// may be `nil`) if the index is in bounds corresponding to the entry at the +// specified index. +func (t *Table) Get(idx uint32) (*Func, error) { + var func_ptr *C.wasm_func_t + ok := C.wasmtime_funcref_table_get(t.ptr(), C.uint32_t(idx), &func_ptr) + runtime.KeepAlive(t) + if ok { + if func_ptr == nil { + return nil, nil + } + return mkFunc(func_ptr, t.freelist, nil), nil + } else { + return nil, errors.New("index out of bounds") + } +} + +// Sets an item in this table at the specified index. +// +// Returns an error if the index is out of bounds. +func (t *Table) Set(idx uint32, val *Func) error { + var func_ptr *C.wasm_func_t + if val != nil { + func_ptr = val.ptr() + } + err := C.wasmtime_funcref_table_set(t.ptr(), C.uint32_t(idx), func_ptr) + runtime.KeepAlive(t) + runtime.KeepAlive(val) + if err == nil { + return nil + } else { + return mkError(err) + } +} + +// Returns the underlying type of this table func (t *Table) Type() *TableType { ptr := C.wasm_table_type(t.ptr()) runtime.KeepAlive(t) diff --git a/table_test.go b/table_test.go new file mode 100644 index 000000000000..c5bbd14fe0bd --- /dev/null +++ b/table_test.go @@ -0,0 +1,88 @@ +package wasmtime + +import "testing" + +func TestTable(t *testing.T) { + store := NewStore(NewEngine()) + ty := NewTableType(NewValType(KindFuncref), Limits{Min: 1, Max: 3}) + table, err := NewTable(store, ty, nil) + if err != nil { + panic(err) + } + if table.Size() != 1 { + panic("wrong size") + } + + f, err := table.Get(0) + if err != nil { + panic(err) + } + if f != nil { + panic("expected nil") + } + f, err = table.Get(1) + if err == nil { + panic("expected error") + } + if f != nil { + panic("expected nil") + } + + err = table.Set(0, nil) + if err != nil { + panic(err) + } + err = table.Set(1, nil) + if err == nil { + panic("expected error") + } + err = table.Set(0, WrapFunc(store, func() {})) + if err != nil { + panic(nil) + } + f, err = table.Get(0) + if err != nil { + panic(err) + } + if f == nil { + panic("expected not nil") + } + + prev_size, err := table.Grow(1, nil) + if err != nil { + panic(err) + } + if prev_size != 1 { + print(prev_size) + panic("bad prev") + } + f, err = table.Get(1) + if err != nil { + panic(err) + } + if f != nil { + panic("expected nil") + } + + called := false + _, err = table.Grow(1, WrapFunc(store, func() { + called = true + })) + if err != nil { + panic(err) + } + f, err = table.Get(2) + if err != nil { + panic(err) + } + if called { + panic("called already?") + } + _, err = f.Call() + if err != nil { + panic(err) + } + if !called { + panic("should have called") + } +}