Flattens Memory and Table types and backfills Table encoder (#356)

This flattens Memory and Table types, particularly making it a
compilation error to add multiple of either.

This also backfills binary encoding of Table.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2022-03-10 15:47:49 +08:00
committed by GitHub
parent 074a2cffbf
commit 97c759b9d8
32 changed files with 586 additions and 413 deletions

View File

@@ -81,7 +81,7 @@ func DecodeModule(binary []byte, features wasm.Features) (*wasm.Module, error) {
case wasm.SectionIDMemory:
m.MemorySection, err = decodeMemorySection(r)
case wasm.SectionIDGlobal:
if m.GlobalSection, err = decodeGlobalSection(r, features); err != nil {
if m.GlobalSection, err = decodeGlobalSection(r); err != nil {
return nil, err // avoid re-wrapping the error.
}
case wasm.SectionIDExport:

View File

@@ -57,16 +57,10 @@ func TestDecodeModule(t *testing.T) {
},
},
{
name: "memory and export section",
name: "table and memory section",
input: &wasm.Module{
MemorySection: []*wasm.MemoryType{{Min: 0, Max: &zero}},
ExportSection: map[string]*wasm.Export{
"mem": {
Name: "mem",
Type: wasm.ExternTypeMemory,
Index: 0,
},
},
TableSection: &wasm.Table{Min: 3},
MemorySection: &wasm.Memory{Min: 1},
},
},
{

View File

@@ -13,6 +13,9 @@ func decodeElementSegment(r *bytes.Reader) (*wasm.ElementSegment, error) {
if err != nil {
return nil, fmt.Errorf("get table index: %w", err)
}
if ti > 0 {
return nil, fmt.Errorf("at most one table allowed in module, but read index %d", ti)
}
expr, err := decodeConstantExpression(r)
if err != nil {
@@ -33,9 +36,5 @@ func decodeElementSegment(r *bytes.Reader) (*wasm.ElementSegment, error) {
init[i] = fIDx
}
return &wasm.ElementSegment{
TableIndex: ti,
OffsetExpr: expr,
Init: init,
}, nil
return &wasm.ElementSegment{OffsetExpr: expr, Init: init}, nil
}

View File

@@ -25,7 +25,7 @@ func EncodeModule(m *wasm.Module) (bytes []byte) {
bytes = append(bytes, encodeFunctionSection(m.FunctionSection)...)
}
if m.SectionElementCount(wasm.SectionIDTable) > 0 {
panic("TODO: TableSection")
bytes = append(bytes, encodeTableSection(m.TableSection)...)
}
if m.SectionElementCount(wasm.SectionIDMemory) > 0 {
bytes = append(bytes, encodeMemorySection(m.MemorySection)...)

View File

@@ -105,6 +105,21 @@ func TestModule_Encode(t *testing.T) {
0x00, // start function index
),
},
{
name: "table and memory section",
input: &wasm.Module{
TableSection: &wasm.Table{Min: 3},
MemorySection: &wasm.Memory{Min: 1},
},
expected: append(append(Magic, version...),
wasm.SectionIDTable, 0x04, // 4 bytes in this section
0x01, // 1 table
wasm.ElemTypeFuncref, 0x0, 0x03, // func, only min: 3
wasm.SectionIDMemory, 0x03, // 3 bytes in this section
0x01, // 1 memory
0x0, 0x01, // only min: 01
),
},
{
name: "exported func with instructions",
input: &wasm.Module{

View File

@@ -1,58 +1,9 @@
package binary
import (
"bytes"
"fmt"
wasm "github.com/tetratelabs/wazero/internal/wasm"
)
func decodeTableType(r *bytes.Reader) (*wasm.TableType, error) {
b, err := r.ReadByte()
if err != nil {
return nil, fmt.Errorf("read leading byte: %v", err)
}
if b != 0x70 {
return nil, fmt.Errorf("%w: invalid element type %#x != %#x", ErrInvalidByte, b, 0x70)
}
lm, err := decodeLimitsType(r)
if err != nil {
return nil, fmt.Errorf("read limits: %v", err)
}
return &wasm.TableType{
ElemType: 0x70, // funcref
Limit: lm,
}, nil
}
func decodeGlobalType(r *bytes.Reader, features wasm.Features) (*wasm.GlobalType, error) {
vt, err := decodeValueTypes(r, 1)
if err != nil {
return nil, fmt.Errorf("read value type: %w", err)
}
ret := &wasm.GlobalType{
ValType: vt[0],
}
b, err := r.ReadByte()
if err != nil {
return nil, fmt.Errorf("read mutablity: %w", err)
}
switch mut := b; mut {
case 0x00: // not mutable
case 0x01: // mutable
ret.Mutable = true
default:
return nil, fmt.Errorf("%w for mutability: %#x != 0x00 or 0x01", ErrInvalidByte, mut)
}
return ret, nil
}
var nullary = []byte{0x60, 0, 0}
// encodedOneParam is a cache of internalwasm.FunctionType values for param length 1 and result length 0

View File

@@ -2,12 +2,16 @@ package binary
import (
"bytes"
"fmt"
wasm "github.com/tetratelabs/wazero/internal/wasm"
)
func decodeGlobal(r *bytes.Reader, features wasm.Features) (*wasm.Global, error) {
gt, err := decodeGlobalType(r, features)
// decodeGlobal returns the wasm.Global decoded with the WebAssembly 1.0 (20191205) Binary Format.
//
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-global
func decodeGlobal(r *bytes.Reader) (*wasm.Global, error) {
gt, err := decodeGlobalType(r)
if err != nil {
return nil, err
}
@@ -20,6 +24,34 @@ func decodeGlobal(r *bytes.Reader, features wasm.Features) (*wasm.Global, error)
return &wasm.Global{Type: gt, Init: init}, nil
}
// decodeGlobalType returns the wasm.GlobalType decoded with the WebAssembly 1.0 (20191205) Binary Format.
//
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-globaltype
func decodeGlobalType(r *bytes.Reader) (*wasm.GlobalType, error) {
vt, err := decodeValueTypes(r, 1)
if err != nil {
return nil, fmt.Errorf("read value type: %w", err)
}
ret := &wasm.GlobalType{
ValType: vt[0],
}
b, err := r.ReadByte()
if err != nil {
return nil, fmt.Errorf("read mutablity: %w", err)
}
switch mut := b; mut {
case 0x00: // not mutable
case 0x01: // mutable
ret.Mutable = true
default:
return nil, fmt.Errorf("%w for mutability: %#x != 0x00 or 0x01", ErrInvalidByte, mut)
}
return ret, nil
}
// encodeGlobal returns the internalwasm.Global encoded in WebAssembly 1.0 (20191205) Binary Format.
//
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#global-section%E2%91%A0

View File

@@ -27,11 +27,11 @@ func decodeImport(r *bytes.Reader, idx uint32, features wasm.Features) (i *wasm.
case wasm.ExternTypeFunc:
i.DescFunc, _, err = leb128.DecodeUint32(r)
case wasm.ExternTypeTable:
i.DescTable, err = decodeTableType(r)
i.DescTable, err = decodeTable(r)
case wasm.ExternTypeMemory:
i.DescMem, err = decodeMemoryType(r)
i.DescMem, err = decodeMemory(r)
case wasm.ExternTypeGlobal:
i.DescGlobal, err = decodeGlobalType(r, features)
i.DescGlobal, err = decodeGlobalType(r)
default:
err = fmt.Errorf("%w: invalid byte for importdesc: %#x", ErrInvalidByte, b)
}

View File

@@ -5,47 +5,48 @@ import (
"fmt"
"github.com/tetratelabs/wazero/internal/leb128"
wasm "github.com/tetratelabs/wazero/internal/wasm"
)
// decodeLimitsType returns the wasm.LimitsType decoded with the WebAssembly 1.0 (20191205) Binary Format.
// decodeLimitsType returns the `limitsType` (min, max) decoded with the WebAssembly 1.0 (20191205) Binary Format.
//
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#limits%E2%91%A6
func decodeLimitsType(r *bytes.Reader) (*wasm.LimitsType, error) {
b, err := r.ReadByte()
if err != nil {
return nil, fmt.Errorf("read leading byte: %v", err)
func decodeLimitsType(r *bytes.Reader) (min uint32, max *uint32, err error) {
var flag byte
if flag, err = r.ReadByte(); err != nil {
err = fmt.Errorf("read leading byte: %v", err)
return
}
ret := &wasm.LimitsType{}
switch b {
switch flag {
case 0x00:
ret.Min, _, err = leb128.DecodeUint32(r)
min, _, err = leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("read min of limit: %v", err)
err = fmt.Errorf("read min of limit: %v", err)
}
case 0x01:
ret.Min, _, err = leb128.DecodeUint32(r)
min, _, err = leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("read min of limit: %v", err)
err = fmt.Errorf("read min of limit: %v", err)
return
}
m, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("read max of limit: %v", err)
var m uint32
if m, _, err = leb128.DecodeUint32(r); err != nil {
err = fmt.Errorf("read max of limit: %v", err)
} else {
max = &m
}
ret.Max = &m
default:
return nil, fmt.Errorf("%v for limits: %#x != 0x00 or 0x01", ErrInvalidByte, b)
err = fmt.Errorf("%v for limits: %#x != 0x00 or 0x01", ErrInvalidByte, flag)
}
return ret, nil
return
}
// encodeLimitsType returns the internalwasm.LimitsType encoded in WebAssembly 1.0 (20191205) Binary Format.
// encodeLimitsType returns the `limitsType` (min, max) encoded in WebAssembly 1.0 (20191205) Binary Format.
//
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#limits%E2%91%A6
func encodeLimitsType(l *wasm.LimitsType) []byte {
if l.Max == nil {
return append(leb128.EncodeUint32(0x00), leb128.EncodeUint32(l.Min)...)
func encodeLimitsType(min uint32, max *uint32) []byte {
if max == nil {
return append(leb128.EncodeUint32(0x00), leb128.EncodeUint32(min)...)
}
return append(leb128.EncodeUint32(0x01), append(leb128.EncodeUint32(l.Min), leb128.EncodeUint32(*l.Max)...)...)
return append(leb128.EncodeUint32(0x01), append(leb128.EncodeUint32(min), leb128.EncodeUint32(*max)...)...)
}

View File

@@ -7,42 +7,41 @@ import (
"testing"
"github.com/stretchr/testify/require"
wasm "github.com/tetratelabs/wazero/internal/wasm"
)
func TestLimitsType(t *testing.T) {
zero := uint32(0)
max := uint32(math.MaxUint32)
largest := uint32(math.MaxUint32)
tests := []struct {
name string
input *wasm.LimitsType
min uint32
max *uint32
expected []byte
}{
{
name: "min 0",
input: &wasm.LimitsType{},
expected: []byte{0x0, 0},
},
{
name: "min 0, max 0",
input: &wasm.LimitsType{Max: &zero},
max: &zero,
expected: []byte{0x1, 0, 0},
},
{
name: "min largest",
input: &wasm.LimitsType{Min: max},
min: largest,
expected: []byte{0x0, 0xff, 0xff, 0xff, 0xff, 0xf},
},
{
name: "min 0, max largest",
input: &wasm.LimitsType{Max: &max},
max: &largest,
expected: []byte{0x1, 0, 0xff, 0xff, 0xff, 0xff, 0xf},
},
{
name: "min largest max largest",
input: &wasm.LimitsType{Min: max, Max: &max},
min: largest,
max: &largest,
expected: []byte{0x1, 0xff, 0xff, 0xff, 0xff, 0xf, 0xff, 0xff, 0xff, 0xff, 0xf},
},
}
@@ -50,15 +49,16 @@ func TestLimitsType(t *testing.T) {
for _, tt := range tests {
tc := tt
b := encodeLimitsType(tc.input)
b := encodeLimitsType(tc.min, tc.max)
t.Run(fmt.Sprintf("encode - %s", tc.name), func(t *testing.T) {
require.Equal(t, tc.expected, b)
})
t.Run(fmt.Sprintf("decode - %s", tc.name), func(t *testing.T) {
decoded, err := decodeLimitsType(bytes.NewReader(b))
min, max, err := decodeLimitsType(bytes.NewReader(b))
require.NoError(t, err)
require.Equal(t, decoded, tc.input)
require.Equal(t, min, tc.min)
require.Equal(t, max, tc.max)
})
}
}

View File

@@ -7,30 +7,30 @@ import (
wasm "github.com/tetratelabs/wazero/internal/wasm"
)
// decodeMemoryType returns the wasm.MemoryType decoded with the WebAssembly 1.0 (20191205) Binary Format.
// decodeMemory returns the wasm.Memory decoded with the WebAssembly 1.0 (20191205) Binary Format.
//
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-memory
func decodeMemoryType(r *bytes.Reader) (*wasm.MemoryType, error) {
ret, err := decodeLimitsType(r)
func decodeMemory(r *bytes.Reader) (*wasm.Memory, error) {
min, max, err := decodeLimitsType(r)
if err != nil {
return nil, err
}
if ret.Min > wasm.MemoryMaxPages {
if min > wasm.MemoryMaxPages {
return nil, fmt.Errorf("memory min must be at most 65536 pages (4GiB)")
}
if ret.Max != nil {
if *ret.Max < ret.Min {
if max != nil {
if *max < min {
return nil, fmt.Errorf("memory size minimum must not be greater than maximum")
} else if *ret.Max > wasm.MemoryMaxPages {
} else if *max > wasm.MemoryMaxPages {
return nil, fmt.Errorf("memory max must be at most 65536 pages (4GiB)")
}
}
return ret, nil
return &wasm.Memory{Min: min, Max: max}, nil
}
// encodeMemoryType returns the internalwasm.MemoryType encoded in WebAssembly 1.0 (20191205) Binary Format.
// encodeMemory returns the internalwasm.Memory encoded in WebAssembly 1.0 (20191205) Binary Format.
//
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-memory
func encodeMemoryType(i *wasm.MemoryType) []byte {
return encodeLimitsType(i)
func encodeMemory(i *wasm.Memory) []byte {
return encodeLimitsType(i.Min, i.Max)
}

View File

@@ -16,32 +16,32 @@ func TestMemoryType(t *testing.T) {
tests := []struct {
name string
input *wasm.MemoryType
input *wasm.Memory
expected []byte
}{
{
name: "min 0",
input: &wasm.MemoryType{},
input: &wasm.Memory{},
expected: []byte{0x0, 0},
},
{
name: "min 0, max 0",
input: &wasm.MemoryType{Max: &zero},
input: &wasm.Memory{Max: &zero},
expected: []byte{0x1, 0, 0},
},
{
name: "min largest",
input: &wasm.MemoryType{Min: max},
input: &wasm.Memory{Min: max},
expected: []byte{0x0, 0x80, 0x80, 0x4},
},
{
name: "min 0, max largest",
input: &wasm.MemoryType{Max: &max},
input: &wasm.Memory{Max: &max},
expected: []byte{0x1, 0, 0x80, 0x80, 0x4},
},
{
name: "min largest max largest",
input: &wasm.MemoryType{Min: max, Max: &max},
input: &wasm.Memory{Min: max, Max: &max},
expected: []byte{0x1, 0x80, 0x80, 0x4, 0x80, 0x80, 0x4},
},
}
@@ -49,13 +49,13 @@ func TestMemoryType(t *testing.T) {
for _, tt := range tests {
tc := tt
b := encodeMemoryType(tc.input)
b := encodeMemory(tc.input)
t.Run(fmt.Sprintf("encode - %s", tc.name), func(t *testing.T) {
require.Equal(t, tc.expected, b)
})
t.Run(fmt.Sprintf("decode - %s", tc.name), func(t *testing.T) {
decoded, err := decodeMemoryType(bytes.NewReader(b))
decoded, err := decodeMemory(bytes.NewReader(b))
require.NoError(t, err)
require.Equal(t, decoded, tc.input)
})
@@ -88,7 +88,7 @@ func TestDecodeMemoryType_Errors(t *testing.T) {
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
_, err := decodeMemoryType(bytes.NewReader(tc.input))
_, err := decodeMemory(bytes.NewReader(tc.input))
require.EqualError(t, err, tc.expectedErr)
})
}

View File

@@ -91,37 +91,31 @@ func decodeFunctionSection(r *bytes.Reader) ([]uint32, error) {
return result, err
}
func decodeTableSection(r *bytes.Reader) ([]*wasm.TableType, error) {
func decodeTableSection(r *bytes.Reader) (*wasm.Table, error) {
vs, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("get size of vector: %w", err)
return nil, fmt.Errorf("error reading size")
}
if vs > 1 {
return nil, fmt.Errorf("at most one table allowed in module, but read %d", vs)
}
result := make([]*wasm.TableType, vs)
for i := uint32(0); i < vs; i++ {
if result[i], err = decodeTableType(r); err != nil {
return nil, fmt.Errorf("read table type: %w", err)
}
}
return result, nil
return decodeTable(r)
}
func decodeMemorySection(r *bytes.Reader) ([]*wasm.MemoryType, error) {
func decodeMemorySection(r *bytes.Reader) (*wasm.Memory, error) {
vs, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("get size of vector: %w", err)
return nil, fmt.Errorf("error reading size")
}
if vs > 1 {
return nil, fmt.Errorf("at most one memory allowed in module, but read %d", vs)
}
result := make([]*wasm.MemoryType, vs)
for i := uint32(0); i < vs; i++ {
if result[i], err = decodeMemoryType(r); err != nil {
return nil, fmt.Errorf("read memory type: %w", err)
}
}
return result, nil
return decodeMemory(r)
}
func decodeGlobalSection(r *bytes.Reader, features wasm.Features) ([]*wasm.Global, error) {
func decodeGlobalSection(r *bytes.Reader) ([]*wasm.Global, error) {
vs, _, err := leb128.DecodeUint32(r)
if err != nil {
return nil, fmt.Errorf("get size of vector: %w", err)
@@ -129,7 +123,7 @@ func decodeGlobalSection(r *bytes.Reader, features wasm.Features) ([]*wasm.Globa
result := make([]*wasm.Global, vs)
for i := uint32(0); i < vs; i++ {
if result[i], err = decodeGlobal(r, features); err != nil {
if result[i], err = decodeGlobal(r); err != nil {
return nil, fmt.Errorf("global[%d]: %w", i, err)
}
}
@@ -266,16 +260,23 @@ func encodeCodeSection(code []*wasm.Code) []byte {
return encodeSection(wasm.SectionIDCode, contents)
}
// encodeTableSection encodes a internalwasm.SectionIDTable for the module-defined function in WebAssembly 1.0
// (20191205) Binary Format.
//
// See encodeTable
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#table-section%E2%91%A0
func encodeTableSection(table *wasm.Table) []byte {
contents := append([]byte{1}, encodeTable(table)...)
return encodeSection(wasm.SectionIDTable, contents)
}
// encodeMemorySection encodes a internalwasm.SectionIDMemory for the module-defined function in WebAssembly 1.0
// (20191205) Binary Format.
//
// See encodeMemoryType
// See encodeMemory
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#memory-section%E2%91%A0
func encodeMemorySection(memories []*wasm.MemoryType) []byte {
contents := leb128.EncodeUint32(uint32(len(memories)))
for _, i := range memories {
contents = append(contents, encodeMemoryType(i)...)
}
func encodeMemorySection(memory *wasm.Memory) []byte {
contents := append([]byte{1}, encodeMemory(memory)...)
return encodeSection(wasm.SectionIDMemory, contents)
}

View File

@@ -9,21 +9,75 @@ import (
wasm "github.com/tetratelabs/wazero/internal/wasm"
)
func TestTableSection(t *testing.T) {
three := uint32(3)
tests := []struct {
name string
input []byte
expected *wasm.Table
}{
{
name: "min and min with max",
input: []byte{
0x01, // 1 table
wasm.ElemTypeFuncref, 0x01, 2, 3, // (table 2 3)
},
expected: &wasm.Table{Min: 2, Max: &three},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
tables, err := decodeTableSection(bytes.NewReader(tc.input))
require.NoError(t, err)
require.Equal(t, tc.expected, tables)
})
}
}
func TestTableSection_Errors(t *testing.T) {
tests := []struct {
name string
input []byte
expectedErr string
}{
{
name: "min and min with max",
input: []byte{
0x02, // 2 tables
wasm.ElemTypeFuncref, 0x00, 0x01, // (table 1)
wasm.ElemTypeFuncref, 0x01, 0x02, 0x03, // (table 2 3)
},
expectedErr: "at most one table allowed in module, but read 2",
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
_, err := decodeTableSection(bytes.NewReader(tc.input))
require.EqualError(t, err, tc.expectedErr)
})
}
}
func TestMemorySection(t *testing.T) {
three := uint32(3)
tests := []struct {
name string
input []byte
expected []*wasm.MemoryType
expected *wasm.Memory
}{
{
name: "min and min with max",
input: []byte{
0x02, // 2 memories
0x00, 1, // (memory 1)
0x01, 2, 3, // (memory 2, 3)
0x01, // 1 memory
0x01, 0x02, 0x03, // (memory 2 3)
},
expected: []*wasm.MemoryType{{Min: 1}, {Min: 2, Max: &three}},
expected: &wasm.Memory{Min: 2, Max: &three},
},
}
@@ -38,6 +92,33 @@ func TestMemorySection(t *testing.T) {
}
}
func TestMemorySection_Errors(t *testing.T) {
tests := []struct {
name string
input []byte
expectedErr string
}{
{
name: "min and min with max",
input: []byte{
0x02, // 2 memories
0x01, // (memory 1)
0x02, 0x03, // (memory 2 3)
},
expectedErr: "at most one memory allowed in module, but read 2",
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
_, err := decodeMemorySection(bytes.NewReader(tc.input))
require.EqualError(t, err, tc.expectedErr)
})
}
}
func TestDecodeExportSection(t *testing.T) {
tests := []struct {
name string

View File

@@ -0,0 +1,45 @@
package binary
import (
"bytes"
"fmt"
wasm "github.com/tetratelabs/wazero/internal/wasm"
)
// decodeTable returns the wasm.Table decoded with the WebAssembly 1.0 (20191205) Binary Format.
//
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-table
func decodeTable(r *bytes.Reader) (*wasm.Table, error) {
b, err := r.ReadByte()
if err != nil {
return nil, fmt.Errorf("read leading byte: %v", err)
}
if b != wasm.ElemTypeFuncref {
return nil, fmt.Errorf("invalid element type %#x != funcref(%#x)", b, wasm.ElemTypeFuncref)
}
min, max, err := decodeLimitsType(r)
if err != nil {
return nil, fmt.Errorf("read limits: %v", err)
}
if min > wasm.MaximumFunctionIndex {
return nil, fmt.Errorf("table min must be at most %d", wasm.MaximumFunctionIndex)
}
if max != nil {
if *max < min {
return nil, fmt.Errorf("table size minimum must not be greater than maximum")
} else if *max > wasm.MaximumFunctionIndex {
return nil, fmt.Errorf("table max must be at most %d", wasm.MaximumFunctionIndex)
}
}
return &wasm.Table{Min: min, Max: max}, nil
}
// encodeTable returns the internalwasm.Table encoded in WebAssembly 1.0 (20191205) Binary Format.
//
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-table
func encodeTable(i *wasm.Table) []byte {
return append([]byte{wasm.ElemTypeFuncref}, encodeLimitsType(i.Min, i.Max)...)
}

View File

@@ -0,0 +1,100 @@
package binary
import (
"bytes"
"fmt"
"testing"
"github.com/stretchr/testify/require"
wasm "github.com/tetratelabs/wazero/internal/wasm"
)
func TestTableType(t *testing.T) {
zero := uint32(0)
max := wasm.MaximumFunctionIndex
tests := []struct {
name string
input *wasm.Table
expected []byte
}{
{
name: "min 0",
input: &wasm.Table{},
expected: []byte{wasm.ElemTypeFuncref, 0x0, 0},
},
{
name: "min 0, max 0",
input: &wasm.Table{Max: &zero},
expected: []byte{wasm.ElemTypeFuncref, 0x1, 0, 0},
},
{
name: "min largest",
input: &wasm.Table{Min: max},
expected: []byte{wasm.ElemTypeFuncref, 0x0, 0x80, 0x80, 0x80, 0x40},
},
{
name: "min 0, max largest",
input: &wasm.Table{Max: &max},
expected: []byte{wasm.ElemTypeFuncref, 0x1, 0, 0x80, 0x80, 0x80, 0x40},
},
{
name: "min largest max largest",
input: &wasm.Table{Min: max, Max: &max},
expected: []byte{wasm.ElemTypeFuncref, 0x1, 0x80, 0x80, 0x80, 0x40, 0x80, 0x80, 0x80, 0x40},
},
}
for _, tt := range tests {
tc := tt
b := encodeTable(tc.input)
t.Run(fmt.Sprintf("encode - %s", tc.name), func(t *testing.T) {
require.Equal(t, tc.expected, b)
})
t.Run(fmt.Sprintf("decode - %s", tc.name), func(t *testing.T) {
decoded, err := decodeTable(bytes.NewReader(b))
require.NoError(t, err)
require.Equal(t, decoded, tc.input)
})
}
}
func TestDecodeTableType_Errors(t *testing.T) {
tests := []struct {
name string
input []byte
expectedErr string
}{
{
name: "not func ref",
input: []byte{0x50, 0x1, 0x80, 0x80, 0x4, 0},
expectedErr: "invalid element type 0x50 != funcref(0x70)",
},
{
name: "max < min",
input: []byte{wasm.ElemTypeFuncref, 0x1, 0x80, 0x80, 0x4, 0},
expectedErr: "table size minimum must not be greater than maximum",
},
{
name: "min > limit",
input: []byte{wasm.ElemTypeFuncref, 0x0, 0xff, 0xff, 0xff, 0xff, 0xf},
expectedErr: "table min must be at most 134217728",
},
{
name: "max > limit",
input: []byte{wasm.ElemTypeFuncref, 0x1, 0, 0xff, 0xff, 0xff, 0xff, 0xf},
expectedErr: "table max must be at most 134217728",
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
_, err := decodeTable(bytes.NewReader(tc.input))
require.EqualError(t, err, tc.expectedErr)
})
}
}