1088 lines
36 KiB
Go
1088 lines
36 KiB
Go
package wasm
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
|
|
"github.com/tetratelabs/wazero/api"
|
|
"github.com/tetratelabs/wazero/internal/leb128"
|
|
"github.com/tetratelabs/wazero/internal/testing/require"
|
|
)
|
|
|
|
func uint32Ptr(v uint32) *uint32 {
|
|
return &v
|
|
}
|
|
|
|
func Test_resolveImports_table(t *testing.T) {
|
|
const moduleName = "test"
|
|
const name = "target"
|
|
|
|
t.Run("ok", func(t *testing.T) {
|
|
max := uint32(10)
|
|
tableInst := &TableInstance{Max: &max}
|
|
modules := map[string]*ModuleInstance{
|
|
moduleName: {
|
|
Tables: []*TableInstance{tableInst},
|
|
Exports: map[string]ExportInstance{name: {Type: ExternTypeTable, Index: 0}},
|
|
Name: moduleName,
|
|
},
|
|
}
|
|
_, _, tables, _, err := resolveImports(&Module{ImportSection: []Import{{Module: moduleName, Name: name, Type: ExternTypeTable, DescTable: Table{Max: &max}}}}, modules)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(tables))
|
|
require.Equal(t, tables[0], tableInst)
|
|
})
|
|
t.Run("minimum size mismatch", func(t *testing.T) {
|
|
importTableType := Table{Min: 2}
|
|
modules := map[string]*ModuleInstance{
|
|
moduleName: {
|
|
Tables: []*TableInstance{{Min: importTableType.Min - 1}},
|
|
Exports: map[string]ExportInstance{name: {Type: ExternTypeTable}},
|
|
Name: moduleName,
|
|
},
|
|
}
|
|
_, _, _, _, err := resolveImports(&Module{ImportSection: []Import{{Module: moduleName, Name: name, Type: ExternTypeTable, DescTable: importTableType}}}, modules)
|
|
require.EqualError(t, err, "import[0] table[test.target]: minimum size mismatch: 2 > 1")
|
|
})
|
|
t.Run("maximum size mismatch", func(t *testing.T) {
|
|
max := uint32(10)
|
|
importTableType := Table{Max: &max}
|
|
modules := map[string]*ModuleInstance{
|
|
moduleName: {
|
|
Tables: []*TableInstance{{Min: importTableType.Min - 1}},
|
|
Exports: map[string]ExportInstance{name: {Type: ExternTypeTable}},
|
|
Name: moduleName,
|
|
},
|
|
}
|
|
_, _, _, _, err := resolveImports(&Module{ImportSection: []Import{{Module: moduleName, Name: name, Type: ExternTypeTable, DescTable: importTableType}}}, modules)
|
|
require.EqualError(t, err, "import[0] table[test.target]: maximum size mismatch: 10, but actual has no max")
|
|
})
|
|
}
|
|
|
|
var codeEnd = &Code{Body: []byte{OpcodeEnd}}
|
|
|
|
func TestModule_validateTable(t *testing.T) {
|
|
const maxTableIndex = 5
|
|
three := uint32(3)
|
|
tests := []struct {
|
|
name string
|
|
input *Module
|
|
expected []validatedActiveElementSegment
|
|
}{
|
|
{
|
|
name: "empty",
|
|
input: &Module{},
|
|
expected: []validatedActiveElementSegment{},
|
|
},
|
|
{
|
|
name: "min zero",
|
|
input: &Module{TableSection: []Table{{}}},
|
|
expected: []validatedActiveElementSegment{},
|
|
},
|
|
{
|
|
name: "maximum number of tables",
|
|
input: &Module{TableSection: []Table{{}, {}, {}, {}, {}}},
|
|
expected: []validatedActiveElementSegment{},
|
|
},
|
|
{
|
|
name: "min/max",
|
|
input: &Module{TableSection: []Table{{Min: 1, Max: &three}}},
|
|
expected: []validatedActiveElementSegment{},
|
|
},
|
|
{ // See: https://github.com/WebAssembly/spec/issues/1427
|
|
name: "constant derived element offset=0 and no index",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
TableSection: []Table{{Min: 1, Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expected: []validatedActiveElementSegment{},
|
|
},
|
|
{
|
|
name: "constant derived element offset=0 and one index",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
TableSection: []Table{{Min: 1, Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
|
|
Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expected: []validatedActiveElementSegment{
|
|
{opcode: OpcodeI32Const, arg: 0, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
{
|
|
name: "constant derived element offset - ignores min on imported table",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{{Type: ExternTypeTable, DescTable: Table{Type: RefTypeFuncref}}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
|
|
Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expected: []validatedActiveElementSegment{
|
|
{opcode: OpcodeI32Const, arg: 0, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
{
|
|
name: "constant derived element offset=0 and one index - imported table",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{{Type: ExternTypeTable, DescTable: Table{Min: 1, Type: RefTypeFuncref}}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
|
|
Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expected: []validatedActiveElementSegment{
|
|
{opcode: OpcodeI32Const, arg: 0, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
{
|
|
name: "constant derived element offset and two indices",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
TableSection: []Table{{Min: 3, Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0, 0, 0, 0},
|
|
CodeSection: []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
|
Init: []*Index{uint32Ptr(0), uint32Ptr(2)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expected: []validatedActiveElementSegment{
|
|
{opcode: OpcodeI32Const, arg: 1, init: []*Index{uint32Ptr(0), uint32Ptr(2)}},
|
|
},
|
|
},
|
|
{ // See: https://github.com/WebAssembly/spec/issues/1427
|
|
name: "imported global derived element offset and no index",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
TableSection: []Table{{Min: 1, Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expected: []validatedActiveElementSegment{},
|
|
},
|
|
{
|
|
name: "imported global derived element offset and one index",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
TableSection: []Table{{Min: 1, Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
|
|
Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expected: []validatedActiveElementSegment{
|
|
{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
{
|
|
name: "imported global derived element offset and one index - imported table",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeTable, DescTable: Table{Min: 1, Type: RefTypeFuncref}},
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
|
|
Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expected: []validatedActiveElementSegment{
|
|
{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
{
|
|
name: "imported global derived element offset - ignores min on imported table",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeTable, DescTable: Table{Type: RefTypeFuncref}},
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
|
|
Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expected: []validatedActiveElementSegment{
|
|
{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
{
|
|
name: "imported global derived element offset - two indices",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI64}},
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
TableSection: []Table{{Min: 3, Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0, 0, 0, 0},
|
|
CodeSection: []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x1}},
|
|
Init: []*Index{uint32Ptr(0), uint32Ptr(2)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expected: []validatedActiveElementSegment{
|
|
{opcode: OpcodeGlobalGet, arg: 1, init: []*Index{uint32Ptr(0), uint32Ptr(2)}},
|
|
},
|
|
},
|
|
{
|
|
name: "mixed elementSegments - const before imported global",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI64}},
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
TableSection: []Table{{Min: 3, Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0, 0, 0, 0},
|
|
CodeSection: []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
|
Init: []*Index{uint32Ptr(0), uint32Ptr(2)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x1}},
|
|
Init: []*Index{uint32Ptr(1), uint32Ptr(2)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expected: []validatedActiveElementSegment{
|
|
{opcode: OpcodeI32Const, arg: 1, init: []*Index{uint32Ptr(0), uint32Ptr(2)}},
|
|
{opcode: OpcodeGlobalGet, arg: 1, init: []*Index{uint32Ptr(1), uint32Ptr(2)}},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, _, _, tables, err := tc.input.AllDeclarations()
|
|
require.NoError(t, err)
|
|
|
|
vt, err := tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.expected, vt)
|
|
|
|
// Ensure it was cached. We have to use Equal not Same because this is a slice, not a pointer.
|
|
require.Equal(t, vt, tc.input.validatedActiveElementSegments)
|
|
vt2, err := tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
|
|
require.NoError(t, err)
|
|
require.Equal(t, vt, vt2)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestModule_validateTable_Errors(t *testing.T) {
|
|
const maxTableIndex = 5
|
|
tests := []struct {
|
|
name string
|
|
input *Module
|
|
expectedErr string
|
|
}{
|
|
{
|
|
name: "too many tables",
|
|
input: &Module{
|
|
TableSection: []Table{{}, {}, {}, {}, {}, {}},
|
|
},
|
|
expectedErr: "too many tables in a module: 6 given with limit 5",
|
|
},
|
|
{
|
|
name: "type mismatch: unknown ref type",
|
|
input: &Module{
|
|
TableSection: []Table{{Type: RefTypeFuncref}},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{
|
|
Opcode: OpcodeI32Const,
|
|
Data: leb128.EncodeUint64(math.MaxUint64),
|
|
},
|
|
Type: 0xff,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element type mismatch: table has funcref but element has unknown(0xff)",
|
|
},
|
|
{
|
|
name: "type mismatch: funcref elem on extern table",
|
|
input: &Module{
|
|
TableSection: []Table{{Type: RefTypeExternref}},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{
|
|
Opcode: OpcodeI32Const,
|
|
Data: leb128.EncodeUint64(math.MaxUint64),
|
|
},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element type mismatch: table has externref but element has funcref",
|
|
},
|
|
{
|
|
name: "type mismatch: extern elem on funcref table",
|
|
input: &Module{
|
|
TableSection: []Table{{Type: RefTypeFuncref}},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{
|
|
Opcode: OpcodeI32Const,
|
|
Data: leb128.EncodeUint64(math.MaxUint64),
|
|
},
|
|
Type: RefTypeExternref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element type mismatch: table has funcref but element has externref",
|
|
},
|
|
{
|
|
name: "non-nil externref",
|
|
input: &Module{
|
|
TableSection: []Table{{Type: RefTypeFuncref}},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{
|
|
Opcode: OpcodeI32Const,
|
|
Data: leb128.EncodeUint64(math.MaxUint64),
|
|
},
|
|
Type: RefTypeExternref,
|
|
Init: []*Index{uint32Ptr(0)},
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element[0].init[0] must be ref.null but was 0",
|
|
},
|
|
{
|
|
name: "constant derived element offset - decode error",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
TableSection: []Table{{Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{
|
|
Opcode: OpcodeI32Const,
|
|
Data: leb128.EncodeUint64(math.MaxUint64),
|
|
},
|
|
Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element[0] couldn't read i32.const parameter: overflows a 32-bit integer",
|
|
},
|
|
{
|
|
name: "constant derived element offset - wrong ValType",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
TableSection: []Table{{Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeI64Const, Data: const0}, Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element[0] has an invalid const expression: i64.const",
|
|
},
|
|
{
|
|
name: "constant derived element offset - missing table",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const0}, Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "unknown table 0 as active element target",
|
|
},
|
|
{
|
|
name: "constant derived element offset exceeds table min",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
TableSection: []Table{{Min: 1, Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)}, Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element[0].init exceeds min table size",
|
|
},
|
|
{
|
|
name: "constant derived element offset puts init beyond table min",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
TableSection: []Table{{Min: 2, Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []*Index{uint32Ptr(0), uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element[1].init exceeds min table size",
|
|
},
|
|
{ // See: https://github.com/WebAssembly/spec/issues/1427
|
|
name: "constant derived element offset beyond table min - no init elements",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
TableSection: []Table{{Min: 1, Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element[0].init exceeds min table size",
|
|
},
|
|
{
|
|
name: "constant derived element offset - funcidx out of range",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
TableSection: []Table{{Min: 1}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []*Index{uint32Ptr(0), uint32Ptr(1)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element[0].init[1] funcidx 1 out of range",
|
|
},
|
|
{
|
|
name: "imported global derived element offset - missing table",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "unknown table 0 as active element target",
|
|
},
|
|
{
|
|
name: "imported global derived element offset - funcidx out of range",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
TableSection: []Table{{Min: 1}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []*Index{uint32Ptr(0), uint32Ptr(1)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element[0].init[1] funcidx 1 out of range",
|
|
},
|
|
{
|
|
name: "imported global derived element offset - wrong ValType",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI64}},
|
|
},
|
|
TableSection: []Table{{Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element[0] (global.get 0): import[0].global.ValType != i32",
|
|
},
|
|
{
|
|
name: "imported global derived element offset - decode error",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
TableSection: []Table{{Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{
|
|
Opcode: OpcodeGlobalGet,
|
|
Data: leb128.EncodeUint64(math.MaxUint64),
|
|
},
|
|
Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element[0] couldn't read global.get parameter: overflows a 32-bit integer",
|
|
},
|
|
{
|
|
name: "imported global derived element offset - no imports",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
TableSection: []Table{{Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0},
|
|
GlobalSection: []Global{{Type: GlobalType{ValType: ValueTypeI32}}}, // ignored as not imported
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element[0] (global.get 0): out of range of imported globals",
|
|
},
|
|
{
|
|
name: "imported global derived element offset - no imports are globals",
|
|
input: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeFunc, DescFunc: 0},
|
|
},
|
|
TableSection: []Table{{Type: RefTypeFuncref}},
|
|
FunctionSection: []Index{0},
|
|
GlobalSection: []Global{{Type: GlobalType{ValType: ValueTypeI32}}}, // ignored as not imported
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []*Index{uint32Ptr(0)},
|
|
Type: RefTypeFuncref,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: "element[0] (global.get 0): out of range of imported globals",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, _, _, tables, err := tc.input.AllDeclarations()
|
|
require.NoError(t, err)
|
|
_, err = tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
|
|
require.EqualError(t, err, tc.expectedErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
var (
|
|
const0 = leb128.EncodeInt32(0)
|
|
const1 = leb128.EncodeInt32(1)
|
|
)
|
|
|
|
func TestModule_buildTables(t *testing.T) {
|
|
three := uint32(3)
|
|
tests := []struct {
|
|
name string
|
|
module *Module
|
|
importedTables []*TableInstance
|
|
importedGlobals []*GlobalInstance
|
|
expectedTables []*TableInstance
|
|
expectedInit []tableInitEntry
|
|
}{
|
|
{
|
|
name: "empty",
|
|
module: &Module{
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{},
|
|
},
|
|
},
|
|
{
|
|
name: "min zero",
|
|
module: &Module{
|
|
TableSection: []Table{{Type: RefTypeFuncref}},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{},
|
|
},
|
|
expectedTables: []*TableInstance{{References: make([]Reference, 0), Min: 0, Type: RefTypeFuncref}},
|
|
},
|
|
{
|
|
name: "min/max",
|
|
module: &Module{
|
|
TableSection: []Table{{Min: 1, Max: &three}},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{},
|
|
},
|
|
expectedTables: []*TableInstance{{References: make([]Reference, 1), Min: 1, Max: &three}},
|
|
},
|
|
{ // See: https://github.com/WebAssembly/spec/issues/1427
|
|
name: "constant derived element offset=0 and no index",
|
|
module: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
TableSection: []Table{{Min: 1}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{},
|
|
},
|
|
expectedTables: []*TableInstance{{References: make([]Reference, 1), Min: 1}},
|
|
},
|
|
{
|
|
name: "null extern refs",
|
|
module: &Module{
|
|
TableSection: []Table{{Min: 10, Type: RefTypeExternref}},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{
|
|
{opcode: OpcodeI32Const, arg: 5, init: []*Index{nil, nil, nil}}, // three null refs.
|
|
},
|
|
},
|
|
expectedTables: []*TableInstance{{References: make([]Reference, 10), Min: 10, Type: RefTypeExternref}},
|
|
expectedInit: []tableInitEntry{{tableIndex: 0, offset: 5, nullExternRefCount: 3}},
|
|
},
|
|
{
|
|
name: "constant derived element offset=0 and one index",
|
|
module: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
TableSection: []Table{{Min: 1}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{
|
|
{opcode: OpcodeI32Const, arg: 0, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
expectedTables: []*TableInstance{{References: make([]Reference, 1), Min: 1}},
|
|
expectedInit: []tableInitEntry{{tableIndex: 0, offset: 0, functionIndexes: []*Index{uint32Ptr(0)}}},
|
|
},
|
|
{
|
|
name: "constant derived element offset - imported table",
|
|
module: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{
|
|
{opcode: OpcodeI32Const, arg: 0, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
importedTables: []*TableInstance{{Min: 2}},
|
|
expectedTables: []*TableInstance{{Min: 2}},
|
|
expectedInit: []tableInitEntry{{tableIndex: 0, offset: 0, functionIndexes: []*Index{uint32Ptr(0)}}},
|
|
},
|
|
{
|
|
name: "constant derived element offset=0 and one index - imported table",
|
|
module: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{{Type: ExternTypeTable, DescTable: Table{Min: 1}}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{
|
|
{opcode: OpcodeI32Const, arg: 0, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
importedTables: []*TableInstance{{Min: 1}},
|
|
expectedTables: []*TableInstance{{Min: 1}},
|
|
expectedInit: []tableInitEntry{{tableIndex: 0, offset: 0, functionIndexes: []*Index{uint32Ptr(0)}}},
|
|
},
|
|
{
|
|
name: "constant derived element offset and two indices",
|
|
module: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
TableSection: []Table{{Min: 3}},
|
|
FunctionSection: []Index{0, 0, 0, 0},
|
|
CodeSection: []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{
|
|
{opcode: OpcodeI32Const, arg: 1, init: []*Index{uint32Ptr(0), uint32Ptr(2)}},
|
|
},
|
|
},
|
|
expectedTables: []*TableInstance{{References: make([]Reference, 3), Min: 3}},
|
|
expectedInit: []tableInitEntry{{tableIndex: 0, offset: 1, functionIndexes: []*Index{uint32Ptr(0), uint32Ptr(2)}}},
|
|
},
|
|
{ // See: https://github.com/WebAssembly/spec/issues/1427
|
|
name: "imported global derived element offset and no index",
|
|
module: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
TableSection: []Table{{Min: 1}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{},
|
|
},
|
|
importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 1}},
|
|
expectedTables: []*TableInstance{{References: make([]Reference, 1), Min: 1}},
|
|
},
|
|
{
|
|
name: "imported global derived element offset and one index",
|
|
module: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
TableSection: []Table{{Min: 2}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{
|
|
{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 1}},
|
|
expectedTables: []*TableInstance{{References: make([]Reference, 2), Min: 2}},
|
|
expectedInit: []tableInitEntry{{tableIndex: 0, offset: 1, functionIndexes: []*Index{uint32Ptr(0)}}},
|
|
},
|
|
{
|
|
name: "imported global derived element offset and one index - imported table",
|
|
module: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeTable, DescTable: Table{Min: 1}},
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{
|
|
{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 1}},
|
|
importedTables: []*TableInstance{{References: make([]Reference, 2), Min: 2}},
|
|
expectedTables: []*TableInstance{{Min: 2, References: []Reference{0, 0}}},
|
|
expectedInit: []tableInitEntry{{tableIndex: 0, offset: 1, functionIndexes: []*Index{uint32Ptr(0)}}},
|
|
},
|
|
{
|
|
name: "imported global derived element offset - ignores min on imported table",
|
|
module: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeTable, DescTable: Table{}},
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{
|
|
{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 1}},
|
|
importedTables: []*TableInstance{{References: make([]Reference, 2), Min: 2}},
|
|
expectedTables: []*TableInstance{{Min: 2, References: []Reference{0, 0}}},
|
|
expectedInit: []tableInitEntry{{tableIndex: 0, offset: 1, functionIndexes: []*Index{uint32Ptr(0)}}},
|
|
},
|
|
{
|
|
name: "imported global derived element offset - two indices",
|
|
module: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI64}},
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
TableSection: []Table{{Min: 3}, {Min: 100}},
|
|
FunctionSection: []Index{0, 0, 0, 0},
|
|
CodeSection: []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
|
|
Init: []*Index{nil, uint32Ptr(2)},
|
|
TableIndex: 1,
|
|
},
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x1}},
|
|
Init: []*Index{uint32Ptr(0), uint32Ptr(2)},
|
|
TableIndex: 0,
|
|
},
|
|
},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{
|
|
{tableIndex: 1, opcode: OpcodeGlobalGet, arg: 0, init: []*Index{nil, uint32Ptr(2)}},
|
|
{tableIndex: 0, opcode: OpcodeGlobalGet, arg: 1, init: []*Index{uint32Ptr(0), uint32Ptr(2)}},
|
|
},
|
|
},
|
|
importedGlobals: []*GlobalInstance{
|
|
{Type: GlobalType{ValType: ValueTypeI64}, Val: 3},
|
|
{Type: GlobalType{ValType: ValueTypeI32}, Val: 1},
|
|
},
|
|
expectedTables: []*TableInstance{
|
|
{References: make([]Reference, 3), Min: 3},
|
|
{References: make([]Reference, 100), Min: 100},
|
|
},
|
|
expectedInit: []tableInitEntry{
|
|
{tableIndex: 1, offset: 3, functionIndexes: []*Index{nil, uint32Ptr(2)}},
|
|
{tableIndex: 0, offset: 1, functionIndexes: []*Index{uint32Ptr(0), uint32Ptr(2)}},
|
|
},
|
|
},
|
|
{
|
|
name: "mixed elementSegments - const before imported global",
|
|
module: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI64}},
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
TableSection: []Table{{Min: 3}},
|
|
FunctionSection: []Index{0, 0, 0, 0},
|
|
CodeSection: []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{
|
|
{opcode: OpcodeI32Const, arg: 1, init: []*Index{uint32Ptr(0), uint32Ptr(2)}},
|
|
{opcode: OpcodeGlobalGet, arg: 1, init: []*Index{uint32Ptr(1), uint32Ptr(2)}},
|
|
},
|
|
},
|
|
importedGlobals: []*GlobalInstance{
|
|
{Type: GlobalType{ValType: ValueTypeI64}, Val: 3},
|
|
{Type: GlobalType{ValType: ValueTypeI32}, Val: 1},
|
|
},
|
|
expectedTables: []*TableInstance{{References: make([]Reference, 3), Min: 3}},
|
|
expectedInit: []tableInitEntry{
|
|
{tableIndex: 0, offset: 1, functionIndexes: []*Index{uint32Ptr(0), uint32Ptr(2)}},
|
|
{tableIndex: 0, offset: 1, functionIndexes: []*Index{uint32Ptr(1), uint32Ptr(2)}},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
tables, init, err := tc.module.buildTables(tc.importedTables, tc.importedGlobals, false)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, tc.expectedTables, tables)
|
|
require.Equal(t, tc.expectedInit, init)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestModule_buildTable_Errors covers the only late error conditions possible.
|
|
func TestModule_buildTable_Errors(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
module *Module
|
|
importedTables []*TableInstance
|
|
importedGlobals []*GlobalInstance
|
|
expectedErr string
|
|
}{
|
|
{
|
|
name: "constant derived element offset exceeds table min - imported table",
|
|
module: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{{Type: ExternTypeTable, DescTable: Table{}}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
|
|
Init: []*Index{uint32Ptr(0)},
|
|
},
|
|
},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{
|
|
{opcode: OpcodeI32Const, arg: 2, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
importedTables: []*TableInstance{{References: make([]Reference, 2), Min: 2}},
|
|
expectedErr: "element[0].init exceeds min table size",
|
|
},
|
|
{
|
|
name: "imported global derived element offset exceeds table min",
|
|
module: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
TableSection: []Table{{Min: 2}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
|
|
Init: []*Index{uint32Ptr(0)},
|
|
},
|
|
},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{
|
|
{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 2}},
|
|
expectedErr: "element[0].init exceeds min table size",
|
|
},
|
|
{
|
|
name: "imported global derived element offset exceeds table min imported table",
|
|
module: &Module{
|
|
TypeSection: []*FunctionType{{}},
|
|
ImportSection: []Import{
|
|
{Type: ExternTypeTable, DescTable: Table{}},
|
|
{Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
|
|
},
|
|
TableSection: []Table{{Min: 2}},
|
|
FunctionSection: []Index{0},
|
|
CodeSection: []*Code{codeEnd},
|
|
ElementSection: []ElementSegment{
|
|
{
|
|
OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
|
|
Init: []*Index{uint32Ptr(0)},
|
|
},
|
|
},
|
|
validatedActiveElementSegments: []validatedActiveElementSegment{
|
|
{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
|
|
},
|
|
},
|
|
importedTables: []*TableInstance{{References: make([]Reference, 2), Min: 2}},
|
|
importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 2}},
|
|
expectedErr: "element[0].init exceeds min table size",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, _, err := tc.module.buildTables(tc.importedTables, tc.importedGlobals, false)
|
|
require.EqualError(t, err, tc.expectedErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTableInstance_Grow(t *testing.T) {
|
|
expOnErr := uint32(0xffff_ffff) // -1 as signed i32.
|
|
max10 := uint32(10)
|
|
tests := []struct {
|
|
name string
|
|
currentLen int
|
|
max *uint32
|
|
delta, exp uint32
|
|
}{
|
|
{
|
|
name: "growing ousside 32-bit range",
|
|
currentLen: 0x10,
|
|
delta: 0xffff_fff0,
|
|
exp: expOnErr,
|
|
},
|
|
{
|
|
name: "growing zero",
|
|
currentLen: 0,
|
|
delta: 0,
|
|
exp: 0,
|
|
},
|
|
{
|
|
name: "growing zero on non zero table",
|
|
currentLen: 5,
|
|
delta: 0,
|
|
exp: 5,
|
|
},
|
|
{
|
|
name: "grow zero on max",
|
|
currentLen: 10,
|
|
delta: 0,
|
|
max: &max10,
|
|
exp: 10,
|
|
},
|
|
{
|
|
name: "grow out of range beyond max",
|
|
currentLen: 10,
|
|
delta: 1,
|
|
max: &max10,
|
|
exp: expOnErr,
|
|
},
|
|
{
|
|
name: "grow out of range beyond max part2",
|
|
currentLen: 10,
|
|
delta: 100,
|
|
max: &max10,
|
|
exp: expOnErr,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tc := tt
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
table := &TableInstance{References: make([]uintptr, tc.currentLen), Max: tc.max}
|
|
actual := table.Grow(tc.delta, 0)
|
|
require.Equal(t, tc.exp, actual)
|
|
})
|
|
}
|
|
}
|