Files
wazero/internal/wasm/binary/element_test.go
2023-05-23 15:09:36 +10:00

474 lines
14 KiB
Go

package binary
import (
"bytes"
"strconv"
"testing"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/internal/testing/require"
"github.com/tetratelabs/wazero/internal/wasm"
)
func Test_ensureElementKindFuncRef(t *testing.T) {
require.NoError(t, ensureElementKindFuncRef(bytes.NewReader([]byte{0x0})))
require.Error(t, ensureElementKindFuncRef(bytes.NewReader([]byte{0x1})))
}
func Test_decodeElementInitValueVector(t *testing.T) {
tests := []struct {
in []byte
exp []wasm.Index
expErr string
}{
{
in: []byte{0},
exp: []wasm.Index{},
},
{
in: []byte{5, 1, 2, 3, 4, 5},
exp: []wasm.Index{1, 2, 3, 4, 5},
},
{
in: []byte{
1,
0xff, 0xff, 0xff, 0xff, 0xf,
},
expErr: "too large function index in Element init: 4294967295",
},
}
for i, tt := range tests {
tc := tt
t.Run(strconv.Itoa(i), func(t *testing.T) {
actual, err := decodeElementInitValueVector(bytes.NewReader(tc.in))
if tc.expErr != "" {
require.EqualError(t, err, tc.expErr)
} else {
require.NoError(t, err)
require.Equal(t, tc.exp, actual)
}
})
}
}
func Test_decodeElementConstExprVector(t *testing.T) {
tests := []struct {
in []byte
refType wasm.RefType
exp []wasm.Index
features api.CoreFeatures
}{
{
in: []byte{0},
exp: []wasm.Index{},
refType: wasm.RefTypeFuncref,
features: api.CoreFeatureBulkMemoryOperations,
},
{
in: []byte{
2, // Two indexes.
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
wasm.OpcodeRefFunc, 100, wasm.OpcodeEnd,
},
exp: []wasm.Index{wasm.ElementInitNullReference, 100},
refType: wasm.RefTypeFuncref,
features: api.CoreFeatureBulkMemoryOperations,
},
{
in: []byte{
4, // Three indexes.
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
wasm.OpcodeRefFunc,
0x80, 0x7f,
wasm.OpcodeEnd,
wasm.OpcodeGlobalGet, 1, wasm.OpcodeEnd,
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
},
exp: []wasm.Index{
wasm.ElementInitNullReference,
16256,
wasm.ElementInitImportedGlobalFunctionReference | 1,
wasm.ElementInitNullReference,
},
refType: wasm.RefTypeFuncref,
features: api.CoreFeatureBulkMemoryOperations,
},
{
in: []byte{
2, // Two indexes.
wasm.OpcodeRefNull, wasm.RefTypeExternref, wasm.OpcodeEnd,
wasm.OpcodeRefNull, wasm.RefTypeExternref, wasm.OpcodeEnd,
},
exp: []wasm.Index{wasm.ElementInitNullReference, wasm.ElementInitNullReference},
refType: wasm.RefTypeExternref,
features: api.CoreFeatureBulkMemoryOperations,
},
}
for i, tt := range tests {
tc := tt
t.Run(strconv.Itoa(i), func(t *testing.T) {
actual, err := decodeElementConstExprVector(bytes.NewReader(tc.in), tc.refType, tc.features)
require.NoError(t, err)
require.Equal(t, tc.exp, actual)
})
}
}
func Test_decodeElementConstExprVector_errors(t *testing.T) {
tests := []struct {
name string
in []byte
refType wasm.RefType
expErr string
features api.CoreFeatures
}{
{
name: "eof",
expErr: "failed to get the size of constexpr vector: EOF",
},
{
name: "feature",
in: []byte{1, wasm.OpcodeRefNull, wasm.RefTypeExternref, wasm.OpcodeEnd},
expErr: "ref.null is not supported as feature \"bulk-memory-operations\" is disabled",
},
{
name: "type mismatch - ref.null",
in: []byte{1, wasm.OpcodeRefNull, wasm.RefTypeExternref, wasm.OpcodeEnd},
refType: wasm.RefTypeFuncref,
features: api.CoreFeaturesV2,
expErr: "element type mismatch: want funcref, but constexpr has externref",
},
{
name: "type mismatch - ref.null",
in: []byte{1, wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd},
refType: wasm.RefTypeExternref,
features: api.CoreFeaturesV2,
expErr: "element type mismatch: want externref, but constexpr has funcref",
},
{
name: "invalid ref type",
in: []byte{1, wasm.OpcodeRefNull, 0xff, wasm.OpcodeEnd},
refType: wasm.RefTypeExternref,
features: api.CoreFeaturesV2,
expErr: "invalid type for ref.null: 0xff",
},
{
name: "type mismatch - ref.fuc",
in: []byte{1, wasm.OpcodeRefFunc, 0, wasm.OpcodeEnd},
refType: wasm.RefTypeExternref,
features: api.CoreFeaturesV2,
expErr: "element type mismatch: want externref, but constexpr has funcref",
},
{
name: "too large index - ref.fuc",
in: []byte{1, wasm.OpcodeRefFunc, 0xff, 0xff, 0xff, 0xff, 0xf, wasm.OpcodeEnd},
refType: wasm.RefTypeFuncref,
features: api.CoreFeaturesV2,
expErr: "too large function index in Element init: 4294967295",
},
{
name: "type mismatch - global.get",
in: []byte{1, wasm.OpcodeGlobalGet, 0, wasm.OpcodeEnd},
refType: wasm.RefTypeExternref,
features: api.CoreFeaturesV2,
expErr: "element type mismatch: want externref, but requires funcref",
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
_, err := decodeElementConstExprVector(bytes.NewReader(tc.in), tc.refType, tc.features)
require.EqualError(t, err, tc.expErr)
})
}
}
func TestDecodeElementSegment(t *testing.T) {
tests := []struct {
name string
in []byte
exp wasm.ElementSegment
expErr string
features api.CoreFeatures
}{
{
name: "legacy",
in: []byte{
0, // Prefix (which is previously the table index fixed to zero)
// Offset const expr.
wasm.OpcodeI32Const, 1, wasm.OpcodeEnd,
// Init vector.
5, 1, 2, 3, 4, 5,
},
exp: wasm.ElementSegment{
OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{1}},
Init: []wasm.Index{1, 2, 3, 4, 5},
Mode: wasm.ElementModeActive,
Type: wasm.RefTypeFuncref,
},
features: api.CoreFeatureBulkMemoryOperations,
},
{
name: "legacy multi byte const expr data",
in: []byte{
0, // Prefix (which is previously the table index fixed to zero)
// Offset const expr.
wasm.OpcodeI32Const, 0x80, 0, wasm.OpcodeEnd,
// Init vector.
5, 1, 2, 3, 4, 5,
},
exp: wasm.ElementSegment{
OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 0}},
Init: []wasm.Index{1, 2, 3, 4, 5},
Mode: wasm.ElementModeActive,
Type: wasm.RefTypeFuncref,
},
features: api.CoreFeatureBulkMemoryOperations,
},
{
name: "passive value vector",
in: []byte{
1, // Prefix.
0, // Elem kind must be fixed to zero.
// Init vector.
5, 1, 2, 3, 4, 5,
},
exp: wasm.ElementSegment{
Init: []wasm.Index{1, 2, 3, 4, 5},
Mode: wasm.ElementModePassive,
Type: wasm.RefTypeFuncref,
},
features: api.CoreFeatureBulkMemoryOperations,
},
{
name: "active with table index encoded.",
in: []byte{
2, // Prefix.
0,
// Offset const expr.
wasm.OpcodeI32Const, 0x80, 0, wasm.OpcodeEnd,
0, // Elem kind must be fixed to zero.
// Init vector.
5, 1, 2, 3, 4, 5,
},
exp: wasm.ElementSegment{
OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 0}},
Init: []wasm.Index{1, 2, 3, 4, 5},
Mode: wasm.ElementModeActive,
Type: wasm.RefTypeFuncref,
},
features: api.CoreFeatureBulkMemoryOperations,
},
{
name: "active with non zero table index encoded.",
in: []byte{
2, // Prefix.
10,
// Offset const expr.
wasm.OpcodeI32Const, 0x80, 0, wasm.OpcodeEnd,
0, // Elem kind must be fixed to zero.
// Init vector.
5, 1, 2, 3, 4, 5,
},
exp: wasm.ElementSegment{
OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 0}},
Init: []wasm.Index{1, 2, 3, 4, 5},
Mode: wasm.ElementModeActive,
Type: wasm.RefTypeFuncref,
TableIndex: 10,
},
features: api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes,
},
{
name: "active with non zero table index encoded but reference-types disabled",
in: []byte{
2, // Prefix.
10,
// Offset const expr.
wasm.OpcodeI32Const, 0x80, 0, wasm.OpcodeEnd,
0, // Elem kind must be fixed to zero.
// Init vector.
5, 1, 2, 3, 4, 5,
},
expErr: `table index must be zero but was 10: feature "reference-types" is disabled`,
features: api.CoreFeatureBulkMemoryOperations,
},
{
name: "declarative",
in: []byte{
3, // Prefix.
0, // Elem kind must be fixed to zero.
// Init vector.
5, 1, 2, 3, 4, 5,
},
exp: wasm.ElementSegment{
Init: []wasm.Index{1, 2, 3, 4, 5},
Mode: wasm.ElementModeDeclarative,
Type: wasm.RefTypeFuncref,
},
features: api.CoreFeatureBulkMemoryOperations,
},
{
name: "active const expr vector",
in: []byte{
4, // Prefix.
// Offset expr.
wasm.OpcodeI32Const, 0x80, 1, wasm.OpcodeEnd,
// Init const expr vector.
3, // number of const expr.
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
wasm.OpcodeRefFunc,
0x80, 0x7f,
wasm.OpcodeEnd,
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
},
exp: wasm.ElementSegment{
OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 1}},
Init: []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference},
Mode: wasm.ElementModeActive,
Type: wasm.RefTypeFuncref,
},
features: api.CoreFeatureBulkMemoryOperations,
},
{
name: "passive const expr vector - funcref",
in: []byte{
5, // Prefix.
wasm.RefTypeFuncref,
// Init const expr vector.
3, // number of const expr.
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
wasm.OpcodeRefFunc,
0x80, 0x7f,
wasm.OpcodeEnd,
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
},
exp: wasm.ElementSegment{
Init: []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference},
Mode: wasm.ElementModePassive,
Type: wasm.RefTypeFuncref,
},
features: api.CoreFeatureBulkMemoryOperations,
},
{
name: "passive const expr vector - unknown ref type",
in: []byte{
5, // Prefix.
0xff,
},
expErr: `ref type must be funcref or externref for element as of WebAssembly 2.0`,
features: api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes,
},
{
name: "active with table index and const expr vector",
in: []byte{
6, // Prefix.
0,
// Offset expr.
wasm.OpcodeI32Const, 0x80, 1, wasm.OpcodeEnd,
wasm.RefTypeFuncref,
// Init const expr vector.
3, // number of const expr.
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
wasm.OpcodeRefFunc,
0x80, 0x7f,
wasm.OpcodeEnd,
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
},
exp: wasm.ElementSegment{
OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 1}},
Init: []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference},
Mode: wasm.ElementModeActive,
Type: wasm.RefTypeFuncref,
},
features: api.CoreFeatureBulkMemoryOperations,
},
{
name: "active with non zero table index and const expr vector",
in: []byte{
6, // Prefix.
10,
// Offset expr.
wasm.OpcodeI32Const, 0x80, 1, wasm.OpcodeEnd,
wasm.RefTypeFuncref,
// Init const expr vector.
3, // number of const expr.
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
wasm.OpcodeRefFunc,
0x80, 0x7f,
wasm.OpcodeEnd,
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
},
exp: wasm.ElementSegment{
OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 1}},
Init: []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference},
Mode: wasm.ElementModeActive,
Type: wasm.RefTypeFuncref,
TableIndex: 10,
},
features: api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes,
},
{
name: "active with non zero table index and const expr vector but feature disabled",
in: []byte{
6, // Prefix.
10,
// Offset expr.
wasm.OpcodeI32Const, 0x80, 1, wasm.OpcodeEnd,
wasm.RefTypeFuncref,
// Init const expr vector.
3, // number of const expr.
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
wasm.OpcodeRefFunc,
0x80, 0x80, 0x80, 0x4f, // 165675008 in varint encoding.
wasm.OpcodeEnd,
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
},
expErr: `table index must be zero but was 10: feature "reference-types" is disabled`,
features: api.CoreFeatureBulkMemoryOperations,
},
{
name: "declarative const expr vector",
in: []byte{
7, // Prefix.
wasm.RefTypeFuncref,
// Init const expr vector.
2, // number of const expr.
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
wasm.OpcodeRefFunc,
0x80, 0x7f,
wasm.OpcodeEnd,
},
exp: wasm.ElementSegment{
Init: []wasm.Index{wasm.ElementInitNullReference, 16256},
Mode: wasm.ElementModeDeclarative,
Type: wasm.RefTypeFuncref,
},
features: api.CoreFeatureBulkMemoryOperations,
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
var actual wasm.ElementSegment
err := decodeElementSegment(bytes.NewReader(tc.in), tc.features, &actual)
if tc.expErr != "" {
require.EqualError(t, err, tc.expErr)
} else {
require.NoError(t, err)
require.Equal(t, actual, tc.exp)
}
})
}
}
func TestDecodeElementSegment_errors(t *testing.T) {
var actual wasm.ElementSegment
err := decodeElementSegment(bytes.NewReader([]byte{1}), api.CoreFeatureMultiValue, &actual)
require.EqualError(t, err, `non-zero prefix for element segment is invalid as feature "bulk-memory-operations" is disabled`)
}