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:
@@ -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:
|
||||
|
||||
@@ -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},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)...)
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)...)...)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
45
internal/wasm/binary/table.go
Normal file
45
internal/wasm/binary/table.go
Normal 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)...)
|
||||
}
|
||||
100
internal/wasm/binary/table_test.go
Normal file
100
internal/wasm/binary/table_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user