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

     1  package wasm
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"testing"
     7  
     8  	"wa-lang.org/wazero/api"
     9  	"wa-lang.org/wazero/internal/leb128"
    10  	"wa-lang.org/wazero/internal/testing/require"
    11  	"wa-lang.org/wazero/internal/u64"
    12  )
    13  
    14  func TestFunctionType_String(t *testing.T) {
    15  	tests := []struct {
    16  		functype *FunctionType
    17  		exp      string
    18  	}{
    19  		{functype: &FunctionType{}, exp: "v_v"},
    20  		{functype: &FunctionType{Params: []ValueType{ValueTypeI32}}, exp: "i32_v"},
    21  		{functype: &FunctionType{Params: []ValueType{ValueTypeI32, ValueTypeF64}}, exp: "i32f64_v"},
    22  		{functype: &FunctionType{Params: []ValueType{ValueTypeF32, ValueTypeI32, ValueTypeF64}}, exp: "f32i32f64_v"},
    23  		{functype: &FunctionType{Results: []ValueType{ValueTypeI64}}, exp: "v_i64"},
    24  		{functype: &FunctionType{Results: []ValueType{ValueTypeI64, ValueTypeF32}}, exp: "v_i64f32"},
    25  		{functype: &FunctionType{Results: []ValueType{ValueTypeF32, ValueTypeI32, ValueTypeF64}}, exp: "v_f32i32f64"},
    26  		{functype: &FunctionType{Params: []ValueType{ValueTypeI32}, Results: []ValueType{ValueTypeI64}}, exp: "i32_i64"},
    27  		{functype: &FunctionType{Params: []ValueType{ValueTypeI64, ValueTypeF32}, Results: []ValueType{ValueTypeI64, ValueTypeF32}}, exp: "i64f32_i64f32"},
    28  		{functype: &FunctionType{Params: []ValueType{ValueTypeI64, ValueTypeF32, ValueTypeF64}, Results: []ValueType{ValueTypeF32, ValueTypeI32, ValueTypeF64}}, exp: "i64f32f64_f32i32f64"},
    29  	}
    30  
    31  	for _, tt := range tests {
    32  		tc := tt
    33  		t.Run(tc.functype.String(), func(t *testing.T) {
    34  			require.Equal(t, tc.exp, tc.functype.String())
    35  			require.Equal(t, tc.exp, tc.functype.key())
    36  			require.Equal(t, tc.exp, tc.functype.string)
    37  		})
    38  	}
    39  }
    40  
    41  func TestSectionIDName(t *testing.T) {
    42  	tests := []struct {
    43  		name     string
    44  		input    SectionID
    45  		expected string
    46  	}{
    47  		{"custom", SectionIDCustom, "custom"},
    48  		{"type", SectionIDType, "type"},
    49  		{"import", SectionIDImport, "import"},
    50  		{"function", SectionIDFunction, "function"},
    51  		{"table", SectionIDTable, "table"},
    52  		{"memory", SectionIDMemory, "memory"},
    53  		{"global", SectionIDGlobal, "global"},
    54  		{"export", SectionIDExport, "export"},
    55  		{"start", SectionIDStart, "start"},
    56  		{"element", SectionIDElement, "element"},
    57  		{"code", SectionIDCode, "code"},
    58  		{"data", SectionIDData, "data"},
    59  		{"unknown", 100, "unknown"},
    60  	}
    61  
    62  	for _, tt := range tests {
    63  		tc := tt
    64  
    65  		t.Run(tc.name, func(t *testing.T) {
    66  			require.Equal(t, tc.expected, SectionIDName(tc.input))
    67  		})
    68  	}
    69  }
    70  
    71  func TestMemory_Validate(t *testing.T) {
    72  	tests := []struct {
    73  		name        string
    74  		mem         *Memory
    75  		expectedErr string
    76  	}{
    77  		{
    78  			name: "ok",
    79  			mem:  &Memory{Min: 2, Cap: 2, Max: 2},
    80  		},
    81  		{
    82  			name:        "cap < min",
    83  			mem:         &Memory{Min: 2, Cap: 1, Max: 2},
    84  			expectedErr: "capacity 1 pages (64 Ki) less than minimum 2 pages (128 Ki)",
    85  		},
    86  		{
    87  			name:        "cap > maxLimit",
    88  			mem:         &Memory{Min: 2, Cap: math.MaxUint32, Max: 2},
    89  			expectedErr: "capacity 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)",
    90  		},
    91  		{
    92  			name:        "max < min",
    93  			mem:         &Memory{Min: 2, Cap: 2, Max: 0, IsMaxEncoded: true},
    94  			expectedErr: "min 2 pages (128 Ki) > max 0 pages (0 Ki)",
    95  		},
    96  		{
    97  			name:        "min > limit",
    98  			mem:         &Memory{Min: math.MaxUint32},
    99  			expectedErr: "min 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)",
   100  		},
   101  		{
   102  			name:        "max > limit",
   103  			mem:         &Memory{Max: math.MaxUint32, IsMaxEncoded: true},
   104  			expectedErr: "max 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)",
   105  		},
   106  	}
   107  
   108  	for _, tt := range tests {
   109  		tc := tt
   110  
   111  		t.Run(tc.name, func(t *testing.T) {
   112  			err := tc.mem.Validate(MemoryLimitPages)
   113  			if tc.expectedErr == "" {
   114  				require.NoError(t, err)
   115  			} else {
   116  				require.EqualError(t, err, tc.expectedErr)
   117  			}
   118  		})
   119  	}
   120  }
   121  
   122  func TestModule_allDeclarations(t *testing.T) {
   123  	tests := []struct {
   124  		module            *Module
   125  		expectedFunctions []Index
   126  		expectedGlobals   []*GlobalType
   127  		expectedMemory    *Memory
   128  		expectedTables    []*Table
   129  	}{
   130  		// Functions.
   131  		{
   132  			module: &Module{
   133  				ImportSection:   []*Import{{Type: ExternTypeFunc, DescFunc: 10000}},
   134  				FunctionSection: []Index{10, 20, 30},
   135  			},
   136  			expectedFunctions: []Index{10000, 10, 20, 30},
   137  		},
   138  		{
   139  			module: &Module{
   140  				FunctionSection: []Index{10, 20, 30},
   141  			},
   142  			expectedFunctions: []Index{10, 20, 30},
   143  		},
   144  		{
   145  			module: &Module{
   146  				ImportSection: []*Import{{Type: ExternTypeFunc, DescFunc: 10000}},
   147  			},
   148  			expectedFunctions: []Index{10000},
   149  		},
   150  		// Globals.
   151  		{
   152  			module: &Module{
   153  				ImportSection: []*Import{{Type: ExternTypeGlobal, DescGlobal: &GlobalType{Mutable: false}}},
   154  				GlobalSection: []*Global{{Type: &GlobalType{Mutable: true}}},
   155  			},
   156  			expectedGlobals: []*GlobalType{{Mutable: false}, {Mutable: true}},
   157  		},
   158  		{
   159  			module: &Module{
   160  				GlobalSection: []*Global{{Type: &GlobalType{Mutable: true}}},
   161  			},
   162  			expectedGlobals: []*GlobalType{{Mutable: true}},
   163  		},
   164  		{
   165  			module: &Module{
   166  				ImportSection: []*Import{{Type: ExternTypeGlobal, DescGlobal: &GlobalType{Mutable: false}}},
   167  			},
   168  			expectedGlobals: []*GlobalType{{Mutable: false}},
   169  		},
   170  		// Memories.
   171  		{
   172  			module: &Module{
   173  				ImportSection: []*Import{{Type: ExternTypeMemory, DescMem: &Memory{Min: 1, Max: 10}}},
   174  			},
   175  			expectedMemory: &Memory{Min: 1, Max: 10},
   176  		},
   177  		{
   178  			module: &Module{
   179  				MemorySection: &Memory{Min: 100},
   180  			},
   181  			expectedMemory: &Memory{Min: 100},
   182  		},
   183  		// Tables.
   184  		{
   185  			module: &Module{
   186  				ImportSection: []*Import{{Type: ExternTypeTable, DescTable: &Table{Min: 1}}},
   187  			},
   188  			expectedTables: []*Table{{Min: 1}},
   189  		},
   190  		{
   191  			module: &Module{
   192  				TableSection: []*Table{{Min: 10}},
   193  			},
   194  			expectedTables: []*Table{{Min: 10}},
   195  		},
   196  	}
   197  
   198  	for i, tt := range tests {
   199  		tc := tt
   200  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
   201  			functions, globals, memory, tables, err := tc.module.AllDeclarations()
   202  			require.NoError(t, err)
   203  			require.Equal(t, tc.expectedFunctions, functions)
   204  			require.Equal(t, tc.expectedGlobals, globals)
   205  			require.Equal(t, tc.expectedTables, tables)
   206  			require.Equal(t, tc.expectedMemory, memory)
   207  		})
   208  	}
   209  }
   210  
   211  func TestValidateConstExpression(t *testing.T) {
   212  	t.Run("invalid opcode", func(t *testing.T) {
   213  		expr := &ConstantExpression{Opcode: OpcodeNop}
   214  		err := validateConstExpression(nil, 0, expr, valueTypeUnknown)
   215  		require.Error(t, err)
   216  	})
   217  	for _, vt := range []ValueType{ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64} {
   218  		t.Run(ValueTypeName(vt), func(t *testing.T) {
   219  			t.Run("valid", func(t *testing.T) {
   220  				expr := &ConstantExpression{}
   221  				switch vt {
   222  				case ValueTypeI32:
   223  					expr.Data = []byte{1}
   224  					expr.Opcode = OpcodeI32Const
   225  				case ValueTypeI64:
   226  					expr.Data = []byte{2}
   227  					expr.Opcode = OpcodeI64Const
   228  				case ValueTypeF32:
   229  					expr.Data = u64.LeBytes(api.EncodeF32(math.MaxFloat32))
   230  					expr.Opcode = OpcodeF32Const
   231  				case ValueTypeF64:
   232  					expr.Data = u64.LeBytes(api.EncodeF64(math.MaxFloat64))
   233  					expr.Opcode = OpcodeF64Const
   234  				}
   235  
   236  				err := validateConstExpression(nil, 0, expr, vt)
   237  				require.NoError(t, err)
   238  			})
   239  			t.Run("invalid", func(t *testing.T) {
   240  				// Empty data must be failure.
   241  				expr := &ConstantExpression{Data: make([]byte, 0)}
   242  				switch vt {
   243  				case ValueTypeI32:
   244  					expr.Opcode = OpcodeI32Const
   245  				case ValueTypeI64:
   246  					expr.Opcode = OpcodeI64Const
   247  				case ValueTypeF32:
   248  					expr.Opcode = OpcodeF32Const
   249  				case ValueTypeF64:
   250  					expr.Opcode = OpcodeF64Const
   251  				}
   252  				err := validateConstExpression(nil, 0, expr, vt)
   253  				require.Error(t, err)
   254  			})
   255  		})
   256  	}
   257  	t.Run("ref types", func(t *testing.T) {
   258  		t.Run("ref.func", func(t *testing.T) {
   259  			expr := &ConstantExpression{Data: []byte{5}, Opcode: OpcodeRefFunc}
   260  			err := validateConstExpression(nil, 10, expr, ValueTypeFuncref)
   261  			require.NoError(t, err)
   262  			err = validateConstExpression(nil, 2, expr, ValueTypeFuncref)
   263  			require.EqualError(t, err, "ref.func index out of range [5] with length 1")
   264  		})
   265  		t.Run("ref.null", func(t *testing.T) {
   266  			err := validateConstExpression(nil, 0,
   267  				&ConstantExpression{Data: []byte{ValueTypeFuncref}, Opcode: OpcodeRefNull},
   268  				ValueTypeFuncref)
   269  			require.NoError(t, err)
   270  			err = validateConstExpression(nil, 0,
   271  				&ConstantExpression{Data: []byte{ValueTypeExternref}, Opcode: OpcodeRefNull},
   272  				ValueTypeExternref)
   273  			require.NoError(t, err)
   274  			err = validateConstExpression(nil, 0,
   275  				&ConstantExpression{Data: []byte{0xff}, Opcode: OpcodeRefNull},
   276  				ValueTypeExternref)
   277  			require.EqualError(t, err, "invalid type for ref.null: 0xff")
   278  		})
   279  	})
   280  	t.Run("global expr", func(t *testing.T) {
   281  		t.Run("failed to read global index", func(t *testing.T) {
   282  			// Empty data for global index is invalid.
   283  			expr := &ConstantExpression{Data: make([]byte, 0), Opcode: OpcodeGlobalGet}
   284  			err := validateConstExpression(nil, 0, expr, valueTypeUnknown)
   285  			require.Error(t, err)
   286  		})
   287  		t.Run("global index out of range", func(t *testing.T) {
   288  			// Data holds the index in leb128 and this time the value exceeds len(globals) (=0).
   289  			expr := &ConstantExpression{Data: []byte{1}, Opcode: OpcodeGlobalGet}
   290  			var globals []*GlobalType
   291  			err := validateConstExpression(globals, 0, expr, valueTypeUnknown)
   292  			require.Error(t, err)
   293  		})
   294  
   295  		t.Run("type mismatch", func(t *testing.T) {
   296  			for _, vt := range []ValueType{
   297  				ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64,
   298  			} {
   299  				t.Run(ValueTypeName(vt), func(t *testing.T) {
   300  					// The index specified in Data equals zero.
   301  					expr := &ConstantExpression{Data: []byte{0}, Opcode: OpcodeGlobalGet}
   302  					globals := []*GlobalType{{ValType: valueTypeUnknown}}
   303  
   304  					err := validateConstExpression(globals, 0, expr, vt)
   305  					require.Error(t, err)
   306  				})
   307  			}
   308  		})
   309  		t.Run("ok", func(t *testing.T) {
   310  			for _, vt := range []ValueType{
   311  				ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64,
   312  			} {
   313  				t.Run(ValueTypeName(vt), func(t *testing.T) {
   314  					// The index specified in Data equals zero.
   315  					expr := &ConstantExpression{Data: []byte{0}, Opcode: OpcodeGlobalGet}
   316  					globals := []*GlobalType{{ValType: vt}}
   317  
   318  					err := validateConstExpression(globals, 0, expr, vt)
   319  					require.NoError(t, err)
   320  				})
   321  			}
   322  		})
   323  	})
   324  }
   325  
   326  func TestModule_Validate_Errors(t *testing.T) {
   327  	zero := Index(0)
   328  	tests := []struct {
   329  		name        string
   330  		input       *Module
   331  		expectedErr string
   332  	}{
   333  		{
   334  			name: "StartSection points to an invalid func",
   335  			input: &Module{
   336  				TypeSection:     nil,
   337  				FunctionSection: []uint32{0},
   338  				CodeSection:     []*Code{{Body: []byte{OpcodeEnd}}},
   339  				StartSection:    &zero,
   340  			},
   341  			expectedErr: "invalid start function: func[0] has an invalid type",
   342  		},
   343  	}
   344  
   345  	for _, tt := range tests {
   346  		tc := tt
   347  
   348  		t.Run(tc.name, func(t *testing.T) {
   349  			err := tc.input.Validate(api.CoreFeaturesV1)
   350  			require.EqualError(t, err, tc.expectedErr)
   351  		})
   352  	}
   353  }
   354  
   355  func TestModule_validateStartSection(t *testing.T) {
   356  	t.Run("no start section", func(t *testing.T) {
   357  		m := Module{}
   358  		err := m.validateStartSection()
   359  		require.NoError(t, err)
   360  	})
   361  
   362  	t.Run("invalid type", func(t *testing.T) {
   363  		for _, ft := range []*FunctionType{
   364  			{Params: []ValueType{ValueTypeI32}},
   365  			{Results: []ValueType{ValueTypeI32}},
   366  			{Params: []ValueType{ValueTypeI32}, Results: []ValueType{ValueTypeI32}},
   367  		} {
   368  			t.Run(ft.String(), func(t *testing.T) {
   369  				index := uint32(0)
   370  				m := Module{StartSection: &index, FunctionSection: []uint32{0}, TypeSection: []*FunctionType{ft}}
   371  				err := m.validateStartSection()
   372  				require.Error(t, err)
   373  			})
   374  		}
   375  	})
   376  	t.Run("imported valid func", func(t *testing.T) {
   377  		index := Index(1)
   378  		m := Module{
   379  			StartSection: &index,
   380  			TypeSection:  []*FunctionType{{}, {Results: []ValueType{ValueTypeI32}}},
   381  			ImportSection: []*Import{
   382  				{Type: ExternTypeFunc, DescFunc: 1},
   383  				// import with index 1 is global but this should be skipped when searching imported functions.
   384  				{Type: ExternTypeGlobal},
   385  				{Type: ExternTypeFunc, DescFunc: 0}, // This one must be selected.
   386  			},
   387  		}
   388  		err := m.validateStartSection()
   389  		require.NoError(t, err)
   390  	})
   391  }
   392  
   393  func TestModule_validateGlobals(t *testing.T) {
   394  	t.Run("too many globals", func(t *testing.T) {
   395  		m := Module{}
   396  		err := m.validateGlobals(make([]*GlobalType, 10), 0, 9)
   397  		require.Error(t, err)
   398  		require.EqualError(t, err, "too many globals in a module")
   399  	})
   400  	t.Run("global index out of range", func(t *testing.T) {
   401  		m := Module{GlobalSection: []*Global{
   402  			{
   403  				Type: &GlobalType{ValType: ValueTypeI32},
   404  				// Trying to reference globals[1] which is not imported.
   405  				Init: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{1}},
   406  			},
   407  		}}
   408  		err := m.validateGlobals(nil, 0, 9)
   409  		require.Error(t, err)
   410  		require.EqualError(t, err, "global index out of range")
   411  	})
   412  	t.Run("invalid const expression", func(t *testing.T) {
   413  		m := Module{GlobalSection: []*Global{
   414  			{
   415  				Type: &GlobalType{ValType: valueTypeUnknown},
   416  				Init: &ConstantExpression{Opcode: OpcodeUnreachable},
   417  			},
   418  		}}
   419  		err := m.validateGlobals(nil, 0, 9)
   420  		require.Error(t, err)
   421  		require.EqualError(t, err, "invalid opcode for const expression: 0x0")
   422  	})
   423  	t.Run("ok", func(t *testing.T) {
   424  		m := Module{GlobalSection: []*Global{
   425  			{
   426  				Type: &GlobalType{ValType: ValueTypeI32},
   427  				Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
   428  			},
   429  		}}
   430  		err := m.validateGlobals(nil, 0, 9)
   431  		require.NoError(t, err)
   432  	})
   433  	t.Run("ok with imported global", func(t *testing.T) {
   434  		m := Module{
   435  			GlobalSection: []*Global{
   436  				{
   437  					Type: &GlobalType{ValType: ValueTypeI32},
   438  					// Trying to reference globals[1] which is imported.
   439  					Init: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0}},
   440  				},
   441  			},
   442  			ImportSection: []*Import{{Type: ExternTypeGlobal}},
   443  		}
   444  		globalDeclarations := []*GlobalType{
   445  			{ValType: ValueTypeI32}, // Imported one.
   446  			nil,                     // the local one trying to validate.
   447  		}
   448  		err := m.validateGlobals(globalDeclarations, 0, 9)
   449  		require.NoError(t, err)
   450  	})
   451  }
   452  
   453  func TestModule_validateFunctions(t *testing.T) {
   454  	t.Run("ok", func(t *testing.T) {
   455  		m := Module{
   456  			TypeSection:     []*FunctionType{v_v},
   457  			FunctionSection: []uint32{0},
   458  			CodeSection:     []*Code{{Body: []byte{OpcodeI32Const, 0, OpcodeDrop, OpcodeEnd}}},
   459  		}
   460  		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
   461  		require.NoError(t, err)
   462  	})
   463  	t.Run("too many functions", func(t *testing.T) {
   464  		m := Module{}
   465  		err := m.validateFunctions(api.CoreFeaturesV1, []uint32{1, 2, 3, 4}, nil, nil, nil, 3)
   466  		require.Error(t, err)
   467  		require.EqualError(t, err, "too many functions in a store")
   468  	})
   469  	t.Run("function, but no code", func(t *testing.T) {
   470  		m := Module{
   471  			TypeSection:     []*FunctionType{v_v},
   472  			FunctionSection: []Index{0},
   473  			CodeSection:     nil,
   474  		}
   475  		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
   476  		require.Error(t, err)
   477  		require.EqualError(t, err, "code count (0) != function count (1)")
   478  	})
   479  	t.Run("function out of range of code", func(t *testing.T) {
   480  		m := Module{
   481  			TypeSection:     []*FunctionType{v_v},
   482  			FunctionSection: []Index{1},
   483  			CodeSection:     []*Code{{Body: []byte{OpcodeEnd}}},
   484  		}
   485  		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
   486  		require.Error(t, err)
   487  		require.EqualError(t, err, "invalid function[0]: type section index 1 out of range")
   488  	})
   489  	t.Run("invalid", func(t *testing.T) {
   490  		m := Module{
   491  			TypeSection:     []*FunctionType{v_v},
   492  			FunctionSection: []Index{0},
   493  			CodeSection:     []*Code{{Body: []byte{OpcodeF32Abs}}},
   494  		}
   495  		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
   496  		require.Error(t, err)
   497  		require.Contains(t, err.Error(), "invalid function[0]: cannot pop the 1st f32 operand")
   498  	})
   499  	t.Run("in- exported", func(t *testing.T) {
   500  		m := Module{
   501  			TypeSection:     []*FunctionType{v_v},
   502  			FunctionSection: []Index{0},
   503  			CodeSection:     []*Code{{Body: []byte{OpcodeF32Abs}}},
   504  			ExportSection:   []*Export{{Name: "f1", Type: ExternTypeFunc, Index: 0}},
   505  		}
   506  		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
   507  		require.Error(t, err)
   508  		require.Contains(t, err.Error(), `invalid function[0] export["f1"]: cannot pop the 1st f32`)
   509  	})
   510  	t.Run("in- exported after import", func(t *testing.T) {
   511  		m := Module{
   512  			TypeSection:     []*FunctionType{v_v},
   513  			ImportSection:   []*Import{{Type: ExternTypeFunc}},
   514  			FunctionSection: []Index{0},
   515  			CodeSection:     []*Code{{Body: []byte{OpcodeF32Abs}}},
   516  			ExportSection:   []*Export{{Name: "f1", Type: ExternTypeFunc, Index: 1}},
   517  		}
   518  		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
   519  		require.Error(t, err)
   520  		require.Contains(t, err.Error(), `invalid function[0] export["f1"]: cannot pop the 1st f32`)
   521  	})
   522  	t.Run("in- exported twice", func(t *testing.T) {
   523  		m := Module{
   524  			TypeSection:     []*FunctionType{v_v},
   525  			FunctionSection: []Index{0},
   526  			CodeSection:     []*Code{{Body: []byte{OpcodeF32Abs}}},
   527  			ExportSection: []*Export{
   528  				{Name: "f1", Type: ExternTypeFunc, Index: 0},
   529  				{Name: "f2", Type: ExternTypeFunc, Index: 0},
   530  			},
   531  		}
   532  		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
   533  		require.Error(t, err)
   534  		require.Contains(t, err.Error(), `invalid function[0] export["f1","f2"]: cannot pop the 1st f32`)
   535  	})
   536  }
   537  
   538  func TestModule_validateMemory(t *testing.T) {
   539  	t.Run("active data segment exits but memory not declared", func(t *testing.T) {
   540  		m := Module{DataSection: []*DataSegment{{OffsetExpression: &ConstantExpression{}}}}
   541  		err := m.validateMemory(nil, nil, api.CoreFeaturesV1)
   542  		require.Error(t, err)
   543  		require.Contains(t, "unknown memory", err.Error())
   544  	})
   545  	t.Run("invalid const expr", func(t *testing.T) {
   546  		m := Module{DataSection: []*DataSegment{{
   547  			OffsetExpression: &ConstantExpression{
   548  				Opcode: OpcodeUnreachable, // Invalid!
   549  			},
   550  		}}}
   551  		err := m.validateMemory(&Memory{}, nil, api.CoreFeaturesV1)
   552  		require.EqualError(t, err, "calculate offset: invalid opcode for const expression: 0x0")
   553  	})
   554  	t.Run("ok", func(t *testing.T) {
   555  		m := Module{DataSection: []*DataSegment{{
   556  			Init: []byte{0x1},
   557  			OffsetExpression: &ConstantExpression{
   558  				Opcode: OpcodeI32Const,
   559  				Data:   leb128.EncodeInt32(1),
   560  			},
   561  		}}}
   562  		err := m.validateMemory(&Memory{}, nil, api.CoreFeaturesV1)
   563  		require.NoError(t, err)
   564  	})
   565  }
   566  
   567  func TestModule_validateImports(t *testing.T) {
   568  	tests := []struct {
   569  		name            string
   570  		enabledFeatures api.CoreFeatures
   571  		i               *Import
   572  		expectedErr     string
   573  	}{
   574  		{name: "empty import section"},
   575  		{
   576  			name:            "func",
   577  			enabledFeatures: api.CoreFeaturesV1,
   578  			i:               &Import{Module: "m", Name: "n", Type: ExternTypeFunc, DescFunc: 0},
   579  		},
   580  		{
   581  			name:            "global var disabled",
   582  			enabledFeatures: api.CoreFeaturesV1.SetEnabled(api.CoreFeatureMutableGlobal, false),
   583  			i: &Import{
   584  				Module:     "m",
   585  				Name:       "n",
   586  				Type:       ExternTypeGlobal,
   587  				DescGlobal: &GlobalType{ValType: ValueTypeI32, Mutable: true},
   588  			},
   589  			expectedErr: `invalid import["m"."n"] global: feature "mutable-global" is disabled`,
   590  		},
   591  		{
   592  			name:            "table",
   593  			enabledFeatures: api.CoreFeaturesV1,
   594  			i: &Import{
   595  				Module:    "m",
   596  				Name:      "n",
   597  				Type:      ExternTypeTable,
   598  				DescTable: &Table{Min: 1},
   599  			},
   600  		},
   601  		{
   602  			name:            "memory",
   603  			enabledFeatures: api.CoreFeaturesV1,
   604  			i: &Import{
   605  				Module:  "m",
   606  				Name:    "n",
   607  				Type:    ExternTypeMemory,
   608  				DescMem: &Memory{Min: 1},
   609  			},
   610  		},
   611  	}
   612  
   613  	for _, tt := range tests {
   614  		tc := tt
   615  		t.Run(tc.name, func(t *testing.T) {
   616  			m := Module{}
   617  			if tc.i != nil {
   618  				m.ImportSection = []*Import{tc.i}
   619  			}
   620  			err := m.validateImports(tc.enabledFeatures)
   621  			if tc.expectedErr != "" {
   622  				require.EqualError(t, err, tc.expectedErr)
   623  			} else {
   624  				require.NoError(t, err)
   625  			}
   626  		})
   627  	}
   628  }
   629  
   630  func TestModule_validateExports(t *testing.T) {
   631  	tests := []struct {
   632  		name            string
   633  		enabledFeatures api.CoreFeatures
   634  		exportSection   []*Export
   635  		functions       []Index
   636  		globals         []*GlobalType
   637  		memory          *Memory
   638  		tables          []*Table
   639  		expectedErr     string
   640  	}{
   641  		{name: "empty export section", exportSection: []*Export{}},
   642  		{
   643  			name:            "func",
   644  			enabledFeatures: api.CoreFeaturesV1,
   645  			exportSection:   []*Export{{Type: ExternTypeFunc, Index: 0}},
   646  			functions:       []Index{100 /* arbitrary type id*/},
   647  		},
   648  		{
   649  			name:            "func out of range",
   650  			enabledFeatures: api.CoreFeaturesV1,
   651  			exportSection:   []*Export{{Type: ExternTypeFunc, Index: 1, Name: "e"}},
   652  			functions:       []Index{100 /* arbitrary type id*/},
   653  			expectedErr:     `unknown function for export["e"]`,
   654  		},
   655  		{
   656  			name:            "global const",
   657  			enabledFeatures: api.CoreFeaturesV1,
   658  			exportSection:   []*Export{{Type: ExternTypeGlobal, Index: 0}},
   659  			globals:         []*GlobalType{{ValType: ValueTypeI32}},
   660  		},
   661  		{
   662  			name:            "global var",
   663  			enabledFeatures: api.CoreFeaturesV1,
   664  			exportSection:   []*Export{{Type: ExternTypeGlobal, Index: 0}},
   665  			globals:         []*GlobalType{{ValType: ValueTypeI32, Mutable: true}},
   666  		},
   667  		{
   668  			name:            "global var disabled",
   669  			enabledFeatures: api.CoreFeaturesV1.SetEnabled(api.CoreFeatureMutableGlobal, false),
   670  			exportSection:   []*Export{{Type: ExternTypeGlobal, Index: 0, Name: "e"}},
   671  			globals:         []*GlobalType{{ValType: ValueTypeI32, Mutable: true}},
   672  			expectedErr:     `invalid export["e"] global[0]: feature "mutable-global" is disabled`,
   673  		},
   674  		{
   675  			name:            "global out of range",
   676  			enabledFeatures: api.CoreFeaturesV1,
   677  			exportSection:   []*Export{{Type: ExternTypeGlobal, Index: 1, Name: "e"}},
   678  			globals:         []*GlobalType{{}},
   679  			expectedErr:     `unknown global for export["e"]`,
   680  		},
   681  		{
   682  			name:            "table",
   683  			enabledFeatures: api.CoreFeaturesV1,
   684  			exportSection:   []*Export{{Type: ExternTypeTable, Index: 0}},
   685  			tables:          []*Table{{}},
   686  		},
   687  		{
   688  			name:            "multiple tables",
   689  			enabledFeatures: api.CoreFeaturesV1,
   690  			exportSection:   []*Export{{Type: ExternTypeTable, Index: 0}, {Type: ExternTypeTable, Index: 1}, {Type: ExternTypeTable, Index: 2}},
   691  			tables:          []*Table{{}, {}, {}},
   692  		},
   693  		{
   694  			name:            "table out of range",
   695  			enabledFeatures: api.CoreFeaturesV1,
   696  			exportSection:   []*Export{{Type: ExternTypeTable, Index: 1, Name: "e"}},
   697  			tables:          []*Table{},
   698  			expectedErr:     `table for export["e"] out of range`,
   699  		},
   700  		{
   701  			name:            "memory",
   702  			enabledFeatures: api.CoreFeaturesV1,
   703  			exportSection:   []*Export{{Type: ExternTypeMemory, Index: 0}},
   704  			memory:          &Memory{},
   705  		},
   706  		{
   707  			name:            "memory out of range",
   708  			enabledFeatures: api.CoreFeaturesV1,
   709  			exportSection:   []*Export{{Type: ExternTypeMemory, Index: 0, Name: "e"}},
   710  			tables:          []*Table{},
   711  			expectedErr:     `memory for export["e"] out of range`,
   712  		},
   713  	}
   714  
   715  	for _, tt := range tests {
   716  		tc := tt
   717  		t.Run(tc.name, func(t *testing.T) {
   718  			m := Module{ExportSection: tc.exportSection}
   719  			err := m.validateExports(tc.enabledFeatures, tc.functions, tc.globals, tc.memory, tc.tables)
   720  			if tc.expectedErr != "" {
   721  				require.EqualError(t, err, tc.expectedErr)
   722  			} else {
   723  				require.NoError(t, err)
   724  			}
   725  		})
   726  	}
   727  }
   728  
   729  func TestModule_buildGlobals(t *testing.T) {
   730  	minusOne := int32(-1)
   731  	m := Module{GlobalSection: []*Global{
   732  		{
   733  			Type: &GlobalType{Mutable: true, ValType: ValueTypeF64},
   734  			Init: &ConstantExpression{
   735  				Opcode: OpcodeF64Const,
   736  				Data:   u64.LeBytes(api.EncodeF64(math.MaxFloat64)),
   737  			},
   738  		},
   739  		{
   740  			Type: &GlobalType{Mutable: false, ValType: ValueTypeI32},
   741  			Init: &ConstantExpression{
   742  				Opcode: OpcodeI32Const,
   743  				Data:   leb128.EncodeInt32(math.MaxInt32),
   744  			},
   745  		},
   746  		{
   747  			Type: &GlobalType{Mutable: false, ValType: ValueTypeI32},
   748  			Init: &ConstantExpression{
   749  				Opcode: OpcodeI32Const,
   750  				Data:   leb128.EncodeInt32(minusOne),
   751  			},
   752  		},
   753  		{
   754  			Type: &GlobalType{Mutable: false, ValType: ValueTypeV128},
   755  			Init: &ConstantExpression{
   756  				Opcode: OpcodeVecV128Const,
   757  				Data: []byte{
   758  					1, 0, 0, 0, 0, 0, 0, 0,
   759  					2, 0, 0, 0, 0, 0, 0, 0,
   760  				},
   761  			},
   762  		},
   763  	}}
   764  
   765  	globals := m.buildGlobals(nil)
   766  	expectedGlobals := []*GlobalInstance{
   767  		{Type: &GlobalType{ValType: ValueTypeF64, Mutable: true}, Val: api.EncodeF64(math.MaxFloat64)},
   768  		{Type: &GlobalType{ValType: ValueTypeI32, Mutable: false}, Val: uint64(int32(math.MaxInt32))},
   769  		// Higher bits are must be zeroed for i32 globals, not signed-extended. See #656.
   770  		{Type: &GlobalType{ValType: ValueTypeI32, Mutable: false}, Val: uint64(uint32(minusOne))},
   771  		{Type: &GlobalType{ValType: ValueTypeV128, Mutable: false}, Val: 0x1, ValHi: 0x2},
   772  	}
   773  	require.Equal(t, expectedGlobals, globals)
   774  }
   775  
   776  func TestModule_buildFunctions(t *testing.T) {
   777  	nopCode := &Code{Body: []byte{OpcodeEnd}}
   778  	m := &Module{
   779  		TypeSection:     []*FunctionType{v_v},
   780  		ImportSection:   []*Import{{Type: ExternTypeFunc}},
   781  		FunctionSection: []Index{0, 0, 0, 0, 0},
   782  		CodeSection:     []*Code{nopCode, nopCode, nopCode, nopCode, nopCode},
   783  		FunctionDefinitionSection: []*FunctionDefinition{
   784  			{index: 0, funcType: v_v},
   785  			{index: 1, funcType: v_v},
   786  			{index: 2, funcType: v_v, name: "two"},
   787  			{index: 3, funcType: v_v},
   788  			{index: 4, funcType: v_v, name: "four"},
   789  			{index: 5, funcType: v_v, name: "five"},
   790  		},
   791  	}
   792  
   793  	// Note: This only returns module-defined functions, not imported ones. That's why the index starts with 1, not 0.
   794  	instance := &ModuleInstance{Name: "counter", TypeIDs: []FunctionTypeID{0}}
   795  	instance.BuildFunctions(m)
   796  	for i, f := range instance.Functions {
   797  		require.Equal(t, i, f.Definition.Index())
   798  		require.Equal(t, nopCode.Body, f.Body)
   799  	}
   800  }
   801  
   802  func TestModule_buildMemoryInstance(t *testing.T) {
   803  	t.Run("nil", func(t *testing.T) {
   804  		m := Module{}
   805  		mem := m.buildMemory()
   806  		require.Nil(t, mem)
   807  	})
   808  	t.Run("non-nil", func(t *testing.T) {
   809  		min := uint32(1)
   810  		max := uint32(10)
   811  		m := Module{MemorySection: &Memory{Min: min, Cap: min, Max: max}}
   812  		mem := m.buildMemory()
   813  		require.Equal(t, min, mem.Min)
   814  		require.Equal(t, max, mem.Max)
   815  	})
   816  }
   817  
   818  func TestModule_validateDataCountSection(t *testing.T) {
   819  	t.Run("ok", func(t *testing.T) {
   820  		for _, m := range []*Module{
   821  			{
   822  				DataSection:      []*DataSegment{},
   823  				DataCountSection: nil,
   824  			},
   825  			{
   826  				DataSection:      []*DataSegment{{}, {}},
   827  				DataCountSection: nil,
   828  			},
   829  		} {
   830  			err := m.validateDataCountSection()
   831  			require.NoError(t, err)
   832  		}
   833  	})
   834  	t.Run("error", func(t *testing.T) {
   835  		count := uint32(1)
   836  		for _, m := range []*Module{
   837  			{
   838  				DataSection:      []*DataSegment{},
   839  				DataCountSection: &count,
   840  			},
   841  			{
   842  				DataSection:      []*DataSegment{{}, {}},
   843  				DataCountSection: &count,
   844  			},
   845  		} {
   846  			err := m.validateDataCountSection()
   847  			require.Error(t, err)
   848  		}
   849  	})
   850  }
   851  
   852  func TestModule_declaredFunctionIndexes(t *testing.T) {
   853  	tests := []struct {
   854  		name   string
   855  		mod    *Module
   856  		exp    map[Index]struct{}
   857  		expErr string
   858  	}{
   859  		{
   860  			name: "empty",
   861  			mod:  &Module{},
   862  			exp:  map[uint32]struct{}{},
   863  		},
   864  		{
   865  			name: "global",
   866  			mod: &Module{
   867  				ExportSection: []*Export{
   868  					{Index: 10, Type: ExternTypeFunc},
   869  					{Index: 1000, Type: ExternTypeGlobal},
   870  				},
   871  			},
   872  			exp: map[uint32]struct{}{10: {}},
   873  		},
   874  		{
   875  			name: "export",
   876  			mod: &Module{
   877  				ExportSection: []*Export{
   878  					{Index: 1000, Type: ExternTypeGlobal},
   879  					{Index: 10, Type: ExternTypeFunc},
   880  				},
   881  			},
   882  			exp: map[uint32]struct{}{10: {}},
   883  		},
   884  		{
   885  			name: "element",
   886  			mod: &Module{
   887  				ElementSection: []*ElementSegment{
   888  					{
   889  						Mode: ElementModeActive,
   890  						Init: []*Index{uint32Ptr(0), nil, uint32Ptr(5)},
   891  					},
   892  					{
   893  						Mode: ElementModeDeclarative,
   894  						Init: []*Index{uint32Ptr(1), nil, uint32Ptr(5)},
   895  					},
   896  					{
   897  						Mode: ElementModePassive,
   898  						Init: []*Index{uint32Ptr(5), uint32Ptr(2), nil, nil},
   899  					},
   900  				},
   901  			},
   902  			exp: map[uint32]struct{}{0: {}, 1: {}, 2: {}, 5: {}},
   903  		},
   904  		{
   905  			name: "all",
   906  			mod: &Module{
   907  				ExportSection: []*Export{
   908  					{Index: 10, Type: ExternTypeGlobal},
   909  					{Index: 1000, Type: ExternTypeFunc},
   910  				},
   911  				GlobalSection: []*Global{
   912  					{
   913  						Init: &ConstantExpression{
   914  							Opcode: OpcodeI32Const, // not funcref.
   915  							Data:   leb128.EncodeInt32(-1),
   916  						},
   917  					},
   918  					{
   919  						Init: &ConstantExpression{
   920  							Opcode: OpcodeRefFunc,
   921  							Data:   leb128.EncodeInt32(123),
   922  						},
   923  					},
   924  				},
   925  				ElementSection: []*ElementSegment{
   926  					{
   927  						Mode: ElementModeActive,
   928  						Init: []*Index{uint32Ptr(0), nil, uint32Ptr(5)},
   929  					},
   930  					{
   931  						Mode: ElementModeDeclarative,
   932  						Init: []*Index{uint32Ptr(1), nil, uint32Ptr(5)},
   933  					},
   934  					{
   935  						Mode: ElementModePassive,
   936  						Init: []*Index{uint32Ptr(5), uint32Ptr(2), nil, nil},
   937  					},
   938  				},
   939  			},
   940  			exp: map[uint32]struct{}{0: {}, 1: {}, 2: {}, 5: {}, 123: {}, 1000: {}},
   941  		},
   942  		{
   943  			mod: &Module{
   944  				GlobalSection: []*Global{
   945  					{
   946  						Init: &ConstantExpression{
   947  							Opcode: OpcodeRefFunc,
   948  							Data:   nil,
   949  						},
   950  					},
   951  				},
   952  			},
   953  			name:   "invalid global",
   954  			expErr: `global[0] failed to initialize: EOF`,
   955  		},
   956  	}
   957  
   958  	for _, tt := range tests {
   959  		tc := tt
   960  		t.Run(tc.name, func(t *testing.T) {
   961  			actual, err := tc.mod.declaredFunctionIndexes()
   962  			if tc.expErr != "" {
   963  				require.EqualError(t, err, tc.expErr)
   964  			} else {
   965  				require.NoError(t, err)
   966  				require.Equal(t, tc.exp, actual)
   967  			}
   968  		})
   969  	}
   970  }