wa-lang.org/wazero@v1.0.2/internal/wasm/table_test.go (about)

     1  package wasm
     2  
     3  import (
     4  	"math"
     5  	"testing"
     6  
     7  	"wa-lang.org/wazero/api"
     8  	"wa-lang.org/wazero/internal/leb128"
     9  	"wa-lang.org/wazero/internal/testing/require"
    10  )
    11  
    12  func uint32Ptr(v uint32) *uint32 {
    13  	return &v
    14  }
    15  
    16  func Test_resolveImports_table(t *testing.T) {
    17  	const moduleName = "test"
    18  	const name = "target"
    19  
    20  	t.Run("ok", func(t *testing.T) {
    21  		max := uint32(10)
    22  		tableInst := &TableInstance{Max: &max}
    23  		modules := map[string]*ModuleInstance{
    24  			moduleName: {Exports: map[string]*ExportInstance{name: {
    25  				Type:  ExternTypeTable,
    26  				Table: tableInst,
    27  			}}, Name: moduleName},
    28  		}
    29  		_, _, tables, _, err := resolveImports(&Module{ImportSection: []*Import{{Module: moduleName, Name: name, Type: ExternTypeTable, DescTable: &Table{Max: &max}}}}, modules)
    30  		require.NoError(t, err)
    31  		require.Equal(t, 1, len(tables))
    32  		require.Equal(t, tables[0], tableInst)
    33  	})
    34  	t.Run("minimum size mismatch", func(t *testing.T) {
    35  		importTableType := &Table{Min: 2}
    36  		modules := map[string]*ModuleInstance{
    37  			moduleName: {Exports: map[string]*ExportInstance{name: {
    38  				Type:  ExternTypeTable,
    39  				Table: &TableInstance{Min: importTableType.Min - 1},
    40  			}}, Name: moduleName},
    41  		}
    42  		_, _, _, _, err := resolveImports(&Module{ImportSection: []*Import{{Module: moduleName, Name: name, Type: ExternTypeTable, DescTable: importTableType}}}, modules)
    43  		require.EqualError(t, err, "import[0] table[test.target]: minimum size mismatch: 2 > 1")
    44  	})
    45  	t.Run("maximum size mismatch", func(t *testing.T) {
    46  		max := uint32(10)
    47  		importTableType := &Table{Max: &max}
    48  		modules := map[string]*ModuleInstance{
    49  			moduleName: {Exports: map[string]*ExportInstance{name: {
    50  				Type:  ExternTypeTable,
    51  				Table: &TableInstance{Min: importTableType.Min - 1},
    52  			}}, Name: moduleName},
    53  		}
    54  		_, _, _, _, err := resolveImports(&Module{ImportSection: []*Import{{Module: moduleName, Name: name, Type: ExternTypeTable, DescTable: importTableType}}}, modules)
    55  		require.EqualError(t, err, "import[0] table[test.target]: maximum size mismatch: 10, but actual has no max")
    56  	})
    57  }
    58  
    59  var codeEnd = &Code{Body: []byte{OpcodeEnd}}
    60  
    61  func TestModule_validateTable(t *testing.T) {
    62  	const maxTableIndex = 5
    63  	three := uint32(3)
    64  	tests := []struct {
    65  		name     string
    66  		input    *Module
    67  		expected []*validatedActiveElementSegment
    68  	}{
    69  		{
    70  			name:     "empty",
    71  			input:    &Module{},
    72  			expected: []*validatedActiveElementSegment{},
    73  		},
    74  		{
    75  			name:     "min zero",
    76  			input:    &Module{TableSection: []*Table{{}}},
    77  			expected: []*validatedActiveElementSegment{},
    78  		},
    79  		{
    80  			name:     "maximum number of tables",
    81  			input:    &Module{TableSection: []*Table{{}, {}, {}, {}, {}}},
    82  			expected: []*validatedActiveElementSegment{},
    83  		},
    84  		{
    85  			name:     "min/max",
    86  			input:    &Module{TableSection: []*Table{{Min: 1, Max: &three}}},
    87  			expected: []*validatedActiveElementSegment{},
    88  		},
    89  		{ // See: https://github.com/WebAssembly/spec/issues/1427
    90  			name: "constant derived element offset=0 and no index",
    91  			input: &Module{
    92  				TypeSection:     []*FunctionType{{}},
    93  				TableSection:    []*Table{{Min: 1, Type: RefTypeFuncref}},
    94  				FunctionSection: []Index{0},
    95  				CodeSection:     []*Code{codeEnd},
    96  				ElementSection: []*ElementSegment{
    97  					{
    98  						OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
    99  						Type:       RefTypeFuncref,
   100  					},
   101  				},
   102  			},
   103  			expected: []*validatedActiveElementSegment{},
   104  		},
   105  		{
   106  			name: "constant derived element offset=0 and one index",
   107  			input: &Module{
   108  				TypeSection:     []*FunctionType{{}},
   109  				TableSection:    []*Table{{Min: 1, Type: RefTypeFuncref}},
   110  				FunctionSection: []Index{0},
   111  				CodeSection:     []*Code{codeEnd},
   112  				ElementSection: []*ElementSegment{
   113  					{
   114  						OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
   115  						Init:       []*Index{uint32Ptr(0)},
   116  						Type:       RefTypeFuncref,
   117  					},
   118  				},
   119  			},
   120  			expected: []*validatedActiveElementSegment{
   121  				{opcode: OpcodeI32Const, arg: 0, init: []*Index{uint32Ptr(0)}},
   122  			},
   123  		},
   124  		{
   125  			name: "constant derived element offset - ignores min on imported table",
   126  			input: &Module{
   127  				TypeSection:     []*FunctionType{{}},
   128  				ImportSection:   []*Import{{Type: ExternTypeTable, DescTable: &Table{Type: RefTypeFuncref}}},
   129  				FunctionSection: []Index{0},
   130  				CodeSection:     []*Code{codeEnd},
   131  				ElementSection: []*ElementSegment{
   132  					{
   133  						OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
   134  						Init:       []*Index{uint32Ptr(0)},
   135  						Type:       RefTypeFuncref,
   136  					},
   137  				},
   138  			},
   139  			expected: []*validatedActiveElementSegment{
   140  				{opcode: OpcodeI32Const, arg: 0, init: []*Index{uint32Ptr(0)}},
   141  			},
   142  		},
   143  		{
   144  			name: "constant derived element offset=0 and one index - imported table",
   145  			input: &Module{
   146  				TypeSection:     []*FunctionType{{}},
   147  				ImportSection:   []*Import{{Type: ExternTypeTable, DescTable: &Table{Min: 1, Type: RefTypeFuncref}}},
   148  				FunctionSection: []Index{0},
   149  				CodeSection:     []*Code{codeEnd},
   150  				ElementSection: []*ElementSegment{
   151  					{
   152  						OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
   153  						Init:       []*Index{uint32Ptr(0)},
   154  						Type:       RefTypeFuncref,
   155  					},
   156  				},
   157  			},
   158  			expected: []*validatedActiveElementSegment{
   159  				{opcode: OpcodeI32Const, arg: 0, init: []*Index{uint32Ptr(0)}},
   160  			},
   161  		},
   162  		{
   163  			name: "constant derived element offset and two indices",
   164  			input: &Module{
   165  				TypeSection:     []*FunctionType{{}},
   166  				TableSection:    []*Table{{Min: 3, Type: RefTypeFuncref}},
   167  				FunctionSection: []Index{0, 0, 0, 0},
   168  				CodeSection:     []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
   169  				ElementSection: []*ElementSegment{
   170  					{
   171  						OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
   172  						Init:       []*Index{uint32Ptr(0), uint32Ptr(2)},
   173  						Type:       RefTypeFuncref,
   174  					},
   175  				},
   176  			},
   177  			expected: []*validatedActiveElementSegment{
   178  				{opcode: OpcodeI32Const, arg: 1, init: []*Index{uint32Ptr(0), uint32Ptr(2)}},
   179  			},
   180  		},
   181  		{ // See: https://github.com/WebAssembly/spec/issues/1427
   182  			name: "imported global derived element offset and no index",
   183  			input: &Module{
   184  				TypeSection: []*FunctionType{{}},
   185  				ImportSection: []*Import{
   186  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   187  				},
   188  				TableSection:    []*Table{{Min: 1, Type: RefTypeFuncref}},
   189  				FunctionSection: []Index{0},
   190  				CodeSection:     []*Code{codeEnd},
   191  				ElementSection: []*ElementSegment{
   192  					{
   193  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
   194  						Type:       RefTypeFuncref,
   195  					},
   196  				},
   197  			},
   198  			expected: []*validatedActiveElementSegment{},
   199  		},
   200  		{
   201  			name: "imported global derived element offset and one index",
   202  			input: &Module{
   203  				TypeSection: []*FunctionType{{}},
   204  				ImportSection: []*Import{
   205  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   206  				},
   207  				TableSection:    []*Table{{Min: 1, Type: RefTypeFuncref}},
   208  				FunctionSection: []Index{0},
   209  				CodeSection:     []*Code{codeEnd},
   210  				ElementSection: []*ElementSegment{
   211  					{
   212  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
   213  						Init:       []*Index{uint32Ptr(0)},
   214  						Type:       RefTypeFuncref,
   215  					},
   216  				},
   217  			},
   218  			expected: []*validatedActiveElementSegment{
   219  				{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
   220  			},
   221  		},
   222  		{
   223  			name: "imported global derived element offset and one index - imported table",
   224  			input: &Module{
   225  				TypeSection: []*FunctionType{{}},
   226  				ImportSection: []*Import{
   227  					{Type: ExternTypeTable, DescTable: &Table{Min: 1, Type: RefTypeFuncref}},
   228  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   229  				},
   230  				FunctionSection: []Index{0},
   231  				CodeSection:     []*Code{codeEnd},
   232  				ElementSection: []*ElementSegment{
   233  					{
   234  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
   235  						Init:       []*Index{uint32Ptr(0)},
   236  						Type:       RefTypeFuncref,
   237  					},
   238  				},
   239  			},
   240  			expected: []*validatedActiveElementSegment{
   241  				{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
   242  			},
   243  		},
   244  		{
   245  			name: "imported global derived element offset - ignores min on imported table",
   246  			input: &Module{
   247  				TypeSection: []*FunctionType{{}},
   248  				ImportSection: []*Import{
   249  					{Type: ExternTypeTable, DescTable: &Table{Type: RefTypeFuncref}},
   250  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   251  				},
   252  				FunctionSection: []Index{0},
   253  				CodeSection:     []*Code{codeEnd},
   254  				ElementSection: []*ElementSegment{
   255  					{
   256  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
   257  						Init:       []*Index{uint32Ptr(0)},
   258  						Type:       RefTypeFuncref,
   259  					},
   260  				},
   261  			},
   262  			expected: []*validatedActiveElementSegment{
   263  				{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
   264  			},
   265  		},
   266  		{
   267  			name: "imported global derived element offset - two indices",
   268  			input: &Module{
   269  				TypeSection: []*FunctionType{{}},
   270  				ImportSection: []*Import{
   271  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI64}},
   272  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   273  				},
   274  				TableSection:    []*Table{{Min: 3, Type: RefTypeFuncref}},
   275  				FunctionSection: []Index{0, 0, 0, 0},
   276  				CodeSection:     []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
   277  				ElementSection: []*ElementSegment{
   278  					{
   279  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x1}},
   280  						Init:       []*Index{uint32Ptr(0), uint32Ptr(2)},
   281  						Type:       RefTypeFuncref,
   282  					},
   283  				},
   284  			},
   285  			expected: []*validatedActiveElementSegment{
   286  				{opcode: OpcodeGlobalGet, arg: 1, init: []*Index{uint32Ptr(0), uint32Ptr(2)}},
   287  			},
   288  		},
   289  		{
   290  			name: "mixed elementSegments - const before imported global",
   291  			input: &Module{
   292  				TypeSection: []*FunctionType{{}},
   293  				ImportSection: []*Import{
   294  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI64}},
   295  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   296  				},
   297  				TableSection:    []*Table{{Min: 3, Type: RefTypeFuncref}},
   298  				FunctionSection: []Index{0, 0, 0, 0},
   299  				CodeSection:     []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
   300  				ElementSection: []*ElementSegment{
   301  					{
   302  						OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
   303  						Init:       []*Index{uint32Ptr(0), uint32Ptr(2)},
   304  						Type:       RefTypeFuncref,
   305  					},
   306  					{
   307  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x1}},
   308  						Init:       []*Index{uint32Ptr(1), uint32Ptr(2)},
   309  						Type:       RefTypeFuncref,
   310  					},
   311  				},
   312  			},
   313  			expected: []*validatedActiveElementSegment{
   314  				{opcode: OpcodeI32Const, arg: 1, init: []*Index{uint32Ptr(0), uint32Ptr(2)}},
   315  				{opcode: OpcodeGlobalGet, arg: 1, init: []*Index{uint32Ptr(1), uint32Ptr(2)}},
   316  			},
   317  		},
   318  	}
   319  
   320  	for _, tt := range tests {
   321  		tc := tt
   322  
   323  		t.Run(tc.name, func(t *testing.T) {
   324  			_, _, _, tables, err := tc.input.AllDeclarations()
   325  			require.NoError(t, err)
   326  
   327  			vt, err := tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
   328  			require.NoError(t, err)
   329  			require.Equal(t, tc.expected, vt)
   330  
   331  			// Ensure it was cached. We have to use Equal not Same because this is a slice, not a pointer.
   332  			require.Equal(t, vt, tc.input.validatedActiveElementSegments)
   333  			vt2, err := tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
   334  			require.NoError(t, err)
   335  			require.Equal(t, vt, vt2)
   336  		})
   337  	}
   338  }
   339  
   340  func TestModule_validateTable_Errors(t *testing.T) {
   341  	const maxTableIndex = 5
   342  	tests := []struct {
   343  		name        string
   344  		input       *Module
   345  		expectedErr string
   346  	}{
   347  		{
   348  			name: "too many tables",
   349  			input: &Module{
   350  				TableSection: []*Table{{}, {}, {}, {}, {}, {}},
   351  			},
   352  			expectedErr: "too many tables in a module: 6 given with limit 5",
   353  		},
   354  		{
   355  			name: "type mismatch: unknown ref type",
   356  			input: &Module{
   357  				TableSection: []*Table{{Type: RefTypeFuncref}},
   358  				ElementSection: []*ElementSegment{
   359  					{
   360  						OffsetExpr: &ConstantExpression{
   361  							Opcode: OpcodeI32Const,
   362  							Data:   leb128.EncodeUint64(math.MaxUint64),
   363  						},
   364  						Type: 0xff,
   365  					},
   366  				},
   367  			},
   368  			expectedErr: "element type mismatch: table has funcref but element has unknown(0xff)",
   369  		},
   370  		{
   371  			name: "type mismatch: funcref elem on extern table",
   372  			input: &Module{
   373  				TableSection: []*Table{{Type: RefTypeExternref}},
   374  				ElementSection: []*ElementSegment{
   375  					{
   376  						OffsetExpr: &ConstantExpression{
   377  							Opcode: OpcodeI32Const,
   378  							Data:   leb128.EncodeUint64(math.MaxUint64),
   379  						},
   380  						Type: RefTypeFuncref,
   381  					},
   382  				},
   383  			},
   384  			expectedErr: "element type mismatch: table has externref but element has funcref",
   385  		},
   386  		{
   387  			name: "type mismatch: extern elem on funcref table",
   388  			input: &Module{
   389  				TableSection: []*Table{{Type: RefTypeFuncref}},
   390  				ElementSection: []*ElementSegment{
   391  					{
   392  						OffsetExpr: &ConstantExpression{
   393  							Opcode: OpcodeI32Const,
   394  							Data:   leb128.EncodeUint64(math.MaxUint64),
   395  						},
   396  						Type: RefTypeExternref,
   397  					},
   398  				},
   399  			},
   400  			expectedErr: "element type mismatch: table has funcref but element has externref",
   401  		},
   402  		{
   403  			name: "non-nil externref",
   404  			input: &Module{
   405  				TableSection: []*Table{{Type: RefTypeFuncref}},
   406  				ElementSection: []*ElementSegment{
   407  					{
   408  						OffsetExpr: &ConstantExpression{
   409  							Opcode: OpcodeI32Const,
   410  							Data:   leb128.EncodeUint64(math.MaxUint64),
   411  						},
   412  						Type: RefTypeExternref,
   413  						Init: []*Index{uint32Ptr(0)},
   414  					},
   415  				},
   416  			},
   417  			expectedErr: "element[0].init[0] must be ref.null but was 0",
   418  		},
   419  		{
   420  			name: "constant derived element offset - decode error",
   421  			input: &Module{
   422  				TypeSection:     []*FunctionType{{}},
   423  				TableSection:    []*Table{{Type: RefTypeFuncref}},
   424  				FunctionSection: []Index{0},
   425  				CodeSection:     []*Code{codeEnd},
   426  				ElementSection: []*ElementSegment{
   427  					{
   428  						OffsetExpr: &ConstantExpression{
   429  							Opcode: OpcodeI32Const,
   430  							Data:   leb128.EncodeUint64(math.MaxUint64),
   431  						},
   432  						Init: []*Index{uint32Ptr(0)},
   433  						Type: RefTypeFuncref,
   434  					},
   435  				},
   436  			},
   437  			expectedErr: "element[0] couldn't read i32.const parameter: overflows a 32-bit integer",
   438  		},
   439  		{
   440  			name: "constant derived element offset - wrong ValType",
   441  			input: &Module{
   442  				TypeSection:     []*FunctionType{{}},
   443  				TableSection:    []*Table{{Type: RefTypeFuncref}},
   444  				FunctionSection: []Index{0},
   445  				CodeSection:     []*Code{codeEnd},
   446  				ElementSection: []*ElementSegment{
   447  					{
   448  						OffsetExpr: &ConstantExpression{Opcode: OpcodeI64Const, Data: const0}, Init: []*Index{uint32Ptr(0)},
   449  						Type: RefTypeFuncref,
   450  					},
   451  				},
   452  			},
   453  			expectedErr: "element[0] has an invalid const expression: i64.const",
   454  		},
   455  		{
   456  			name: "constant derived element offset - missing table",
   457  			input: &Module{
   458  				TypeSection:     []*FunctionType{{}},
   459  				FunctionSection: []Index{0},
   460  				CodeSection:     []*Code{codeEnd},
   461  				ElementSection: []*ElementSegment{
   462  					{
   463  						OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0}, Init: []*Index{uint32Ptr(0)},
   464  						Type: RefTypeFuncref,
   465  					},
   466  				},
   467  			},
   468  			expectedErr: "unknown table 0 as active element target",
   469  		},
   470  		{
   471  			name: "constant derived element offset exceeds table min",
   472  			input: &Module{
   473  				TypeSection:     []*FunctionType{{}},
   474  				TableSection:    []*Table{{Min: 1, Type: RefTypeFuncref}},
   475  				FunctionSection: []Index{0},
   476  				CodeSection:     []*Code{codeEnd},
   477  				ElementSection: []*ElementSegment{
   478  					{
   479  						OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)}, Init: []*Index{uint32Ptr(0)},
   480  						Type: RefTypeFuncref,
   481  					},
   482  				},
   483  			},
   484  			expectedErr: "element[0].init exceeds min table size",
   485  		},
   486  		{
   487  			name: "constant derived element offset puts init beyond table min",
   488  			input: &Module{
   489  				TypeSection:     []*FunctionType{{}},
   490  				TableSection:    []*Table{{Min: 2, Type: RefTypeFuncref}},
   491  				FunctionSection: []Index{0},
   492  				CodeSection:     []*Code{codeEnd},
   493  				ElementSection: []*ElementSegment{
   494  					{
   495  						OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []*Index{uint32Ptr(0)},
   496  						Type: RefTypeFuncref,
   497  					},
   498  					{
   499  						OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []*Index{uint32Ptr(0), uint32Ptr(0)},
   500  						Type: RefTypeFuncref,
   501  					},
   502  				},
   503  			},
   504  			expectedErr: "element[1].init exceeds min table size",
   505  		},
   506  		{ // See: https://github.com/WebAssembly/spec/issues/1427
   507  			name: "constant derived element offset beyond table min - no init elements",
   508  			input: &Module{
   509  				TypeSection:     []*FunctionType{{}},
   510  				TableSection:    []*Table{{Min: 1, Type: RefTypeFuncref}},
   511  				FunctionSection: []Index{0},
   512  				CodeSection:     []*Code{codeEnd},
   513  				ElementSection: []*ElementSegment{
   514  					{
   515  						OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)},
   516  						Type:       RefTypeFuncref,
   517  					},
   518  				},
   519  			},
   520  			expectedErr: "element[0].init exceeds min table size",
   521  		},
   522  		{
   523  			name: "constant derived element offset - funcidx out of range",
   524  			input: &Module{
   525  				TypeSection:     []*FunctionType{{}},
   526  				TableSection:    []*Table{{Min: 1}},
   527  				FunctionSection: []Index{0},
   528  				CodeSection:     []*Code{codeEnd},
   529  				ElementSection: []*ElementSegment{
   530  					{
   531  						OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []*Index{uint32Ptr(0), uint32Ptr(1)},
   532  						Type: RefTypeFuncref,
   533  					},
   534  				},
   535  			},
   536  			expectedErr: "element[0].init[1] funcidx 1 out of range",
   537  		},
   538  		{
   539  			name: "imported global derived element offset - missing table",
   540  			input: &Module{
   541  				TypeSection: []*FunctionType{{}},
   542  				ImportSection: []*Import{
   543  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   544  				},
   545  				FunctionSection: []Index{0},
   546  				CodeSection:     []*Code{codeEnd},
   547  				ElementSection: []*ElementSegment{
   548  					{
   549  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []*Index{uint32Ptr(0)},
   550  						Type: RefTypeFuncref,
   551  					},
   552  				},
   553  			},
   554  			expectedErr: "unknown table 0 as active element target",
   555  		},
   556  		{
   557  			name: "imported global derived element offset - funcidx out of range",
   558  			input: &Module{
   559  				TypeSection: []*FunctionType{{}},
   560  				ImportSection: []*Import{
   561  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   562  				},
   563  				TableSection:    []*Table{{Min: 1}},
   564  				FunctionSection: []Index{0},
   565  				CodeSection:     []*Code{codeEnd},
   566  				ElementSection: []*ElementSegment{
   567  					{
   568  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []*Index{uint32Ptr(0), uint32Ptr(1)},
   569  						Type: RefTypeFuncref,
   570  					},
   571  				},
   572  			},
   573  			expectedErr: "element[0].init[1] funcidx 1 out of range",
   574  		},
   575  		{
   576  			name: "imported global derived element offset - wrong ValType",
   577  			input: &Module{
   578  				TypeSection: []*FunctionType{{}},
   579  				ImportSection: []*Import{
   580  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI64}},
   581  				},
   582  				TableSection:    []*Table{{Type: RefTypeFuncref}},
   583  				FunctionSection: []Index{0},
   584  				CodeSection:     []*Code{codeEnd},
   585  				ElementSection: []*ElementSegment{
   586  					{
   587  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []*Index{uint32Ptr(0)},
   588  						Type: RefTypeFuncref,
   589  					},
   590  				},
   591  			},
   592  			expectedErr: "element[0] (global.get 0): import[0].global.ValType != i32",
   593  		},
   594  		{
   595  			name: "imported global derived element offset - decode error",
   596  			input: &Module{
   597  				TypeSection: []*FunctionType{{}},
   598  				ImportSection: []*Import{
   599  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   600  				},
   601  				TableSection:    []*Table{{Type: RefTypeFuncref}},
   602  				FunctionSection: []Index{0},
   603  				CodeSection:     []*Code{codeEnd},
   604  				ElementSection: []*ElementSegment{
   605  					{
   606  						OffsetExpr: &ConstantExpression{
   607  							Opcode: OpcodeGlobalGet,
   608  							Data:   leb128.EncodeUint64(math.MaxUint64),
   609  						},
   610  						Init: []*Index{uint32Ptr(0)},
   611  						Type: RefTypeFuncref,
   612  					},
   613  				},
   614  			},
   615  			expectedErr: "element[0] couldn't read global.get parameter: overflows a 32-bit integer",
   616  		},
   617  		{
   618  			name: "imported global derived element offset - no imports",
   619  			input: &Module{
   620  				TypeSection:     []*FunctionType{{}},
   621  				TableSection:    []*Table{{Type: RefTypeFuncref}},
   622  				FunctionSection: []Index{0},
   623  				GlobalSection:   []*Global{{Type: &GlobalType{ValType: ValueTypeI32}}}, // ignored as not imported
   624  				CodeSection:     []*Code{codeEnd},
   625  				ElementSection: []*ElementSegment{
   626  					{
   627  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []*Index{uint32Ptr(0)},
   628  						Type: RefTypeFuncref,
   629  					},
   630  				},
   631  			},
   632  			expectedErr: "element[0] (global.get 0): out of range of imported globals",
   633  		},
   634  		{
   635  			name: "imported global derived element offset - no imports are globals",
   636  			input: &Module{
   637  				TypeSection: []*FunctionType{{}},
   638  				ImportSection: []*Import{
   639  					{Type: ExternTypeFunc, DescFunc: 0},
   640  				},
   641  				TableSection:    []*Table{{Type: RefTypeFuncref}},
   642  				FunctionSection: []Index{0},
   643  				GlobalSection:   []*Global{{Type: &GlobalType{ValType: ValueTypeI32}}}, // ignored as not imported
   644  				CodeSection:     []*Code{codeEnd},
   645  				ElementSection: []*ElementSegment{
   646  					{
   647  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []*Index{uint32Ptr(0)},
   648  						Type: RefTypeFuncref,
   649  					},
   650  				},
   651  			},
   652  			expectedErr: "element[0] (global.get 0): out of range of imported globals",
   653  		},
   654  	}
   655  
   656  	for _, tt := range tests {
   657  		tc := tt
   658  
   659  		t.Run(tc.name, func(t *testing.T) {
   660  			_, _, _, tables, err := tc.input.AllDeclarations()
   661  			require.NoError(t, err)
   662  			_, err = tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
   663  			require.EqualError(t, err, tc.expectedErr)
   664  		})
   665  	}
   666  }
   667  
   668  var (
   669  	const0 = leb128.EncodeInt32(0)
   670  	const1 = leb128.EncodeInt32(1)
   671  )
   672  
   673  func TestModule_buildTables(t *testing.T) {
   674  	three := uint32(3)
   675  	tests := []struct {
   676  		name            string
   677  		module          *Module
   678  		importedTables  []*TableInstance
   679  		importedGlobals []*GlobalInstance
   680  		expectedTables  []*TableInstance
   681  		expectedInit    []tableInitEntry
   682  	}{
   683  		{
   684  			name: "empty",
   685  			module: &Module{
   686  				validatedActiveElementSegments: []*validatedActiveElementSegment{},
   687  			},
   688  		},
   689  		{
   690  			name: "min zero",
   691  			module: &Module{
   692  				TableSection:                   []*Table{{Type: RefTypeFuncref}},
   693  				validatedActiveElementSegments: []*validatedActiveElementSegment{},
   694  			},
   695  			expectedTables: []*TableInstance{{References: make([]Reference, 0), Min: 0, Type: RefTypeFuncref}},
   696  		},
   697  		{
   698  			name: "min/max",
   699  			module: &Module{
   700  				TableSection:                   []*Table{{Min: 1, Max: &three}},
   701  				validatedActiveElementSegments: []*validatedActiveElementSegment{},
   702  			},
   703  			expectedTables: []*TableInstance{{References: make([]Reference, 1), Min: 1, Max: &three}},
   704  		},
   705  		{ // See: https://github.com/WebAssembly/spec/issues/1427
   706  			name: "constant derived element offset=0 and no index",
   707  			module: &Module{
   708  				TypeSection:                    []*FunctionType{{}},
   709  				TableSection:                   []*Table{{Min: 1}},
   710  				FunctionSection:                []Index{0},
   711  				CodeSection:                    []*Code{codeEnd},
   712  				validatedActiveElementSegments: []*validatedActiveElementSegment{},
   713  			},
   714  			expectedTables: []*TableInstance{{References: make([]Reference, 1), Min: 1}},
   715  		},
   716  		{
   717  			name: "null extern refs",
   718  			module: &Module{
   719  				TableSection: []*Table{{Min: 10, Type: RefTypeExternref}},
   720  				validatedActiveElementSegments: []*validatedActiveElementSegment{
   721  					{opcode: OpcodeI32Const, arg: 5, init: []*Index{nil, nil, nil}}, // three null refs.
   722  				},
   723  			},
   724  			expectedTables: []*TableInstance{{References: make([]Reference, 10), Min: 10, Type: RefTypeExternref}},
   725  			expectedInit:   []tableInitEntry{{tableIndex: 0, offset: 5, nullExternRefCount: 3}},
   726  		},
   727  		{
   728  			name: "constant derived element offset=0 and one index",
   729  			module: &Module{
   730  				TypeSection:     []*FunctionType{{}},
   731  				TableSection:    []*Table{{Min: 1}},
   732  				FunctionSection: []Index{0},
   733  				CodeSection:     []*Code{codeEnd},
   734  				validatedActiveElementSegments: []*validatedActiveElementSegment{
   735  					{opcode: OpcodeI32Const, arg: 0, init: []*Index{uint32Ptr(0)}},
   736  				},
   737  			},
   738  			expectedTables: []*TableInstance{{References: make([]Reference, 1), Min: 1}},
   739  			expectedInit:   []tableInitEntry{{tableIndex: 0, offset: 0, functionIndexes: []*Index{uint32Ptr(0)}}},
   740  		},
   741  		{
   742  			name: "constant derived element offset - imported table",
   743  			module: &Module{
   744  				TypeSection:     []*FunctionType{{}},
   745  				FunctionSection: []Index{0},
   746  				CodeSection:     []*Code{codeEnd},
   747  				validatedActiveElementSegments: []*validatedActiveElementSegment{
   748  					{opcode: OpcodeI32Const, arg: 0, init: []*Index{uint32Ptr(0)}},
   749  				},
   750  			},
   751  			importedTables: []*TableInstance{{Min: 2}},
   752  			expectedTables: []*TableInstance{{Min: 2}},
   753  			expectedInit:   []tableInitEntry{{tableIndex: 0, offset: 0, functionIndexes: []*Index{uint32Ptr(0)}}},
   754  		},
   755  		{
   756  			name: "constant derived element offset=0 and one index - imported table",
   757  			module: &Module{
   758  				TypeSection:     []*FunctionType{{}},
   759  				ImportSection:   []*Import{{Type: ExternTypeTable, DescTable: &Table{Min: 1}}},
   760  				FunctionSection: []Index{0},
   761  				CodeSection:     []*Code{codeEnd},
   762  				validatedActiveElementSegments: []*validatedActiveElementSegment{
   763  					{opcode: OpcodeI32Const, arg: 0, init: []*Index{uint32Ptr(0)}},
   764  				},
   765  			},
   766  			importedTables: []*TableInstance{{Min: 1}},
   767  			expectedTables: []*TableInstance{{Min: 1}},
   768  			expectedInit:   []tableInitEntry{{tableIndex: 0, offset: 0, functionIndexes: []*Index{uint32Ptr(0)}}},
   769  		},
   770  		{
   771  			name: "constant derived element offset and two indices",
   772  			module: &Module{
   773  				TypeSection:     []*FunctionType{{}},
   774  				TableSection:    []*Table{{Min: 3}},
   775  				FunctionSection: []Index{0, 0, 0, 0},
   776  				CodeSection:     []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
   777  				validatedActiveElementSegments: []*validatedActiveElementSegment{
   778  					{opcode: OpcodeI32Const, arg: 1, init: []*Index{uint32Ptr(0), uint32Ptr(2)}},
   779  				},
   780  			},
   781  			expectedTables: []*TableInstance{{References: make([]Reference, 3), Min: 3}},
   782  			expectedInit:   []tableInitEntry{{tableIndex: 0, offset: 1, functionIndexes: []*Index{uint32Ptr(0), uint32Ptr(2)}}},
   783  		},
   784  		{ // See: https://github.com/WebAssembly/spec/issues/1427
   785  			name: "imported global derived element offset and no index",
   786  			module: &Module{
   787  				TypeSection: []*FunctionType{{}},
   788  				ImportSection: []*Import{
   789  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   790  				},
   791  				TableSection:                   []*Table{{Min: 1}},
   792  				FunctionSection:                []Index{0},
   793  				CodeSection:                    []*Code{codeEnd},
   794  				validatedActiveElementSegments: []*validatedActiveElementSegment{},
   795  			},
   796  			importedGlobals: []*GlobalInstance{{Type: &GlobalType{ValType: ValueTypeI32}, Val: 1}},
   797  			expectedTables:  []*TableInstance{{References: make([]Reference, 1), Min: 1}},
   798  		},
   799  		{
   800  			name: "imported global derived element offset and one index",
   801  			module: &Module{
   802  				TypeSection: []*FunctionType{{}},
   803  				ImportSection: []*Import{
   804  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   805  				},
   806  				TableSection:    []*Table{{Min: 2}},
   807  				FunctionSection: []Index{0},
   808  				CodeSection:     []*Code{codeEnd},
   809  				validatedActiveElementSegments: []*validatedActiveElementSegment{
   810  					{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
   811  				},
   812  			},
   813  			importedGlobals: []*GlobalInstance{{Type: &GlobalType{ValType: ValueTypeI32}, Val: 1}},
   814  			expectedTables:  []*TableInstance{{References: make([]Reference, 2), Min: 2}},
   815  			expectedInit:    []tableInitEntry{{tableIndex: 0, offset: 1, functionIndexes: []*Index{uint32Ptr(0)}}},
   816  		},
   817  		{
   818  			name: "imported global derived element offset and one index - imported table",
   819  			module: &Module{
   820  				TypeSection: []*FunctionType{{}},
   821  				ImportSection: []*Import{
   822  					{Type: ExternTypeTable, DescTable: &Table{Min: 1}},
   823  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   824  				},
   825  				FunctionSection: []Index{0},
   826  				CodeSection:     []*Code{codeEnd},
   827  				validatedActiveElementSegments: []*validatedActiveElementSegment{
   828  					{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
   829  				},
   830  			},
   831  			importedGlobals: []*GlobalInstance{{Type: &GlobalType{ValType: ValueTypeI32}, Val: 1}},
   832  			importedTables:  []*TableInstance{{References: make([]Reference, 2), Min: 2}},
   833  			expectedTables:  []*TableInstance{{Min: 2, References: []Reference{0, 0}}},
   834  			expectedInit:    []tableInitEntry{{tableIndex: 0, offset: 1, functionIndexes: []*Index{uint32Ptr(0)}}},
   835  		},
   836  		{
   837  			name: "imported global derived element offset - ignores min on imported table",
   838  			module: &Module{
   839  				TypeSection: []*FunctionType{{}},
   840  				ImportSection: []*Import{
   841  					{Type: ExternTypeTable, DescTable: &Table{}},
   842  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   843  				},
   844  				FunctionSection: []Index{0},
   845  				CodeSection:     []*Code{codeEnd},
   846  				validatedActiveElementSegments: []*validatedActiveElementSegment{
   847  					{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
   848  				},
   849  			},
   850  			importedGlobals: []*GlobalInstance{{Type: &GlobalType{ValType: ValueTypeI32}, Val: 1}},
   851  			importedTables:  []*TableInstance{{References: make([]Reference, 2), Min: 2}},
   852  			expectedTables:  []*TableInstance{{Min: 2, References: []Reference{0, 0}}},
   853  			expectedInit:    []tableInitEntry{{tableIndex: 0, offset: 1, functionIndexes: []*Index{uint32Ptr(0)}}},
   854  		},
   855  		{
   856  			name: "imported global derived element offset - two indices",
   857  			module: &Module{
   858  				TypeSection: []*FunctionType{{}},
   859  				ImportSection: []*Import{
   860  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI64}},
   861  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   862  				},
   863  				TableSection:    []*Table{{Min: 3}, {Min: 100}},
   864  				FunctionSection: []Index{0, 0, 0, 0},
   865  				CodeSection:     []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
   866  				ElementSection: []*ElementSegment{
   867  					{
   868  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
   869  						Init:       []*Index{nil, uint32Ptr(2)},
   870  						TableIndex: 1,
   871  					},
   872  					{
   873  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x1}},
   874  						Init:       []*Index{uint32Ptr(0), uint32Ptr(2)},
   875  						TableIndex: 0,
   876  					},
   877  				},
   878  				validatedActiveElementSegments: []*validatedActiveElementSegment{
   879  					{tableIndex: 1, opcode: OpcodeGlobalGet, arg: 0, init: []*Index{nil, uint32Ptr(2)}},
   880  					{tableIndex: 0, opcode: OpcodeGlobalGet, arg: 1, init: []*Index{uint32Ptr(0), uint32Ptr(2)}},
   881  				},
   882  			},
   883  			importedGlobals: []*GlobalInstance{
   884  				{Type: &GlobalType{ValType: ValueTypeI64}, Val: 3},
   885  				{Type: &GlobalType{ValType: ValueTypeI32}, Val: 1},
   886  			},
   887  			expectedTables: []*TableInstance{
   888  				{References: make([]Reference, 3), Min: 3},
   889  				{References: make([]Reference, 100), Min: 100},
   890  			},
   891  			expectedInit: []tableInitEntry{
   892  				{tableIndex: 1, offset: 3, functionIndexes: []*Index{nil, uint32Ptr(2)}},
   893  				{tableIndex: 0, offset: 1, functionIndexes: []*Index{uint32Ptr(0), uint32Ptr(2)}},
   894  			},
   895  		},
   896  		{
   897  			name: "mixed elementSegments - const before imported global",
   898  			module: &Module{
   899  				TypeSection: []*FunctionType{{}},
   900  				ImportSection: []*Import{
   901  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI64}},
   902  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   903  				},
   904  				TableSection:    []*Table{{Min: 3}},
   905  				FunctionSection: []Index{0, 0, 0, 0},
   906  				CodeSection:     []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
   907  				validatedActiveElementSegments: []*validatedActiveElementSegment{
   908  					{opcode: OpcodeI32Const, arg: 1, init: []*Index{uint32Ptr(0), uint32Ptr(2)}},
   909  					{opcode: OpcodeGlobalGet, arg: 1, init: []*Index{uint32Ptr(1), uint32Ptr(2)}},
   910  				},
   911  			},
   912  			importedGlobals: []*GlobalInstance{
   913  				{Type: &GlobalType{ValType: ValueTypeI64}, Val: 3},
   914  				{Type: &GlobalType{ValType: ValueTypeI32}, Val: 1},
   915  			},
   916  			expectedTables: []*TableInstance{{References: make([]Reference, 3), Min: 3}},
   917  			expectedInit: []tableInitEntry{
   918  				{tableIndex: 0, offset: 1, functionIndexes: []*Index{uint32Ptr(0), uint32Ptr(2)}},
   919  				{tableIndex: 0, offset: 1, functionIndexes: []*Index{uint32Ptr(1), uint32Ptr(2)}},
   920  			},
   921  		},
   922  	}
   923  
   924  	for _, tt := range tests {
   925  		tc := tt
   926  
   927  		t.Run(tc.name, func(t *testing.T) {
   928  			tables, init, err := tc.module.buildTables(tc.importedTables, tc.importedGlobals, false)
   929  			require.NoError(t, err)
   930  
   931  			require.Equal(t, tc.expectedTables, tables)
   932  			require.Equal(t, tc.expectedInit, init)
   933  		})
   934  	}
   935  }
   936  
   937  // TestModule_buildTable_Errors covers the only late error conditions possible.
   938  func TestModule_buildTable_Errors(t *testing.T) {
   939  	tests := []struct {
   940  		name            string
   941  		module          *Module
   942  		importedTables  []*TableInstance
   943  		importedGlobals []*GlobalInstance
   944  		expectedErr     string
   945  	}{
   946  		{
   947  			name: "constant derived element offset exceeds table min - imported table",
   948  			module: &Module{
   949  				TypeSection:     []*FunctionType{{}},
   950  				ImportSection:   []*Import{{Type: ExternTypeTable, DescTable: &Table{}}},
   951  				FunctionSection: []Index{0},
   952  				CodeSection:     []*Code{codeEnd},
   953  				ElementSection: []*ElementSegment{
   954  					{
   955  						OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
   956  						Init:       []*Index{uint32Ptr(0)},
   957  					},
   958  				},
   959  				validatedActiveElementSegments: []*validatedActiveElementSegment{
   960  					{opcode: OpcodeI32Const, arg: 2, init: []*Index{uint32Ptr(0)}},
   961  				},
   962  			},
   963  			importedTables: []*TableInstance{{References: make([]Reference, 2), Min: 2}},
   964  			expectedErr:    "element[0].init exceeds min table size",
   965  		},
   966  		{
   967  			name: "imported global derived element offset exceeds table min",
   968  			module: &Module{
   969  				TypeSection: []*FunctionType{{}},
   970  				ImportSection: []*Import{
   971  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   972  				},
   973  				TableSection:    []*Table{{Min: 2}},
   974  				FunctionSection: []Index{0},
   975  				CodeSection:     []*Code{codeEnd},
   976  				ElementSection: []*ElementSegment{
   977  					{
   978  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
   979  						Init:       []*Index{uint32Ptr(0)},
   980  					},
   981  				},
   982  				validatedActiveElementSegments: []*validatedActiveElementSegment{
   983  					{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
   984  				},
   985  			},
   986  			importedGlobals: []*GlobalInstance{{Type: &GlobalType{ValType: ValueTypeI32}, Val: 2}},
   987  			expectedErr:     "element[0].init exceeds min table size",
   988  		},
   989  		{
   990  			name: "imported global derived element offset exceeds table min imported table",
   991  			module: &Module{
   992  				TypeSection: []*FunctionType{{}},
   993  				ImportSection: []*Import{
   994  					{Type: ExternTypeTable, DescTable: &Table{}},
   995  					{Type: ExternTypeGlobal, DescGlobal: &GlobalType{ValType: ValueTypeI32}},
   996  				},
   997  				TableSection:    []*Table{{Min: 2}},
   998  				FunctionSection: []Index{0},
   999  				CodeSection:     []*Code{codeEnd},
  1000  				ElementSection: []*ElementSegment{
  1001  					{
  1002  						OffsetExpr: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
  1003  						Init:       []*Index{uint32Ptr(0)},
  1004  					},
  1005  				},
  1006  				validatedActiveElementSegments: []*validatedActiveElementSegment{
  1007  					{opcode: OpcodeGlobalGet, arg: 0, init: []*Index{uint32Ptr(0)}},
  1008  				},
  1009  			},
  1010  			importedTables:  []*TableInstance{{References: make([]Reference, 2), Min: 2}},
  1011  			importedGlobals: []*GlobalInstance{{Type: &GlobalType{ValType: ValueTypeI32}, Val: 2}},
  1012  			expectedErr:     "element[0].init exceeds min table size",
  1013  		},
  1014  	}
  1015  
  1016  	for _, tt := range tests {
  1017  		tc := tt
  1018  
  1019  		t.Run(tc.name, func(t *testing.T) {
  1020  			_, _, err := tc.module.buildTables(tc.importedTables, tc.importedGlobals, false)
  1021  			require.EqualError(t, err, tc.expectedErr)
  1022  		})
  1023  	}
  1024  }
  1025  
  1026  func TestTableInstance_Grow(t *testing.T) {
  1027  	expOnErr := uint32(0xffff_ffff) // -1 as signed i32.
  1028  	max10 := uint32(10)
  1029  	tests := []struct {
  1030  		name       string
  1031  		currentLen int
  1032  		max        *uint32
  1033  		delta, exp uint32
  1034  	}{
  1035  		{
  1036  			name:       "growing ousside 32-bit range",
  1037  			currentLen: 0x10,
  1038  			delta:      0xffff_fff0,
  1039  			exp:        expOnErr,
  1040  		},
  1041  		{
  1042  			name:       "growing zero",
  1043  			currentLen: 0,
  1044  			delta:      0,
  1045  			exp:        0,
  1046  		},
  1047  		{
  1048  			name:       "growing zero on non zero table",
  1049  			currentLen: 5,
  1050  			delta:      0,
  1051  			exp:        5,
  1052  		},
  1053  		{
  1054  			name:       "grow zero on max",
  1055  			currentLen: 10,
  1056  			delta:      0,
  1057  			max:        &max10,
  1058  			exp:        10,
  1059  		},
  1060  		{
  1061  			name:       "grow out of range beyond max",
  1062  			currentLen: 10,
  1063  			delta:      1,
  1064  			max:        &max10,
  1065  			exp:        expOnErr,
  1066  		},
  1067  		{
  1068  			name:       "grow out of range beyond max part2",
  1069  			currentLen: 10,
  1070  			delta:      100,
  1071  			max:        &max10,
  1072  			exp:        expOnErr,
  1073  		},
  1074  	}
  1075  
  1076  	for _, tt := range tests {
  1077  		tc := tt
  1078  		t.Run(tc.name, func(t *testing.T) {
  1079  			table := &TableInstance{References: make([]uintptr, tc.currentLen), Max: tc.max}
  1080  			actual := table.Grow(testCtx, tc.delta, 0)
  1081  			require.Equal(t, tc.exp, actual)
  1082  		})
  1083  	}
  1084  }