github.com/tetratelabs/wazero@v1.2.1/internal/wasm/module_test.go (about)

     1  package wasm
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"testing"
     7  
     8  	"github.com/tetratelabs/wazero/api"
     9  	"github.com/tetratelabs/wazero/internal/leb128"
    10  	"github.com/tetratelabs/wazero/internal/testing/require"
    11  	"github.com/tetratelabs/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  			ImportFunctionCount: 2,
   382  			ImportSection: []Import{
   383  				{Type: ExternTypeFunc, DescFunc: 1},
   384  				// import with index 1 is global but this should be skipped when searching imported functions.
   385  				{Type: ExternTypeGlobal},
   386  				{Type: ExternTypeFunc, DescFunc: 0}, // This one must be selected.
   387  			},
   388  		}
   389  		err := m.validateStartSection()
   390  		require.NoError(t, err)
   391  	})
   392  }
   393  
   394  func TestModule_validateGlobals(t *testing.T) {
   395  	t.Run("too many globals", func(t *testing.T) {
   396  		m := Module{}
   397  		err := m.validateGlobals(make([]GlobalType, 10), 0, 9)
   398  		require.Error(t, err)
   399  		require.EqualError(t, err, "too many globals in a module")
   400  	})
   401  	t.Run("global index out of range", func(t *testing.T) {
   402  		m := Module{GlobalSection: []Global{
   403  			{
   404  				Type: GlobalType{ValType: ValueTypeI32},
   405  				// Trying to reference globals[1] which is not imported.
   406  				Init: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{1}},
   407  			},
   408  		}}
   409  		err := m.validateGlobals(nil, 0, 9)
   410  		require.Error(t, err)
   411  		require.EqualError(t, err, "global index out of range")
   412  	})
   413  	t.Run("invalid const expression", func(t *testing.T) {
   414  		m := Module{GlobalSection: []Global{
   415  			{
   416  				Type: GlobalType{ValType: valueTypeUnknown},
   417  				Init: ConstantExpression{Opcode: OpcodeUnreachable},
   418  			},
   419  		}}
   420  		err := m.validateGlobals(nil, 0, 9)
   421  		require.Error(t, err)
   422  		require.EqualError(t, err, "invalid opcode for const expression: 0x0")
   423  	})
   424  	t.Run("ok", func(t *testing.T) {
   425  		m := Module{GlobalSection: []Global{
   426  			{
   427  				Type: GlobalType{ValType: ValueTypeI32},
   428  				Init: ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
   429  			},
   430  		}}
   431  		err := m.validateGlobals(nil, 0, 9)
   432  		require.NoError(t, err)
   433  	})
   434  	t.Run("ok with imported global", func(t *testing.T) {
   435  		m := Module{
   436  			ImportGlobalCount: 1,
   437  			GlobalSection: []Global{
   438  				{
   439  					Type: GlobalType{ValType: ValueTypeI32},
   440  					// Trying to reference globals[1] which is imported.
   441  					Init: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0}},
   442  				},
   443  			},
   444  			ImportSection: []Import{{Type: ExternTypeGlobal}},
   445  		}
   446  		globalDeclarations := []GlobalType{
   447  			{ValType: ValueTypeI32}, // Imported one.
   448  			{},                      // the local one trying to validate.
   449  		}
   450  		err := m.validateGlobals(globalDeclarations, 0, 9)
   451  		require.NoError(t, err)
   452  	})
   453  }
   454  
   455  func TestModule_validateFunctions(t *testing.T) {
   456  	t.Run("ok", func(t *testing.T) {
   457  		m := Module{
   458  			TypeSection:     []FunctionType{v_v},
   459  			FunctionSection: []uint32{0},
   460  			CodeSection:     []Code{{Body: []byte{OpcodeI32Const, 0, OpcodeDrop, OpcodeEnd}}},
   461  		}
   462  		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
   463  		require.NoError(t, err)
   464  	})
   465  	t.Run("too many functions", func(t *testing.T) {
   466  		m := Module{}
   467  		err := m.validateFunctions(api.CoreFeaturesV1, []uint32{1, 2, 3, 4}, nil, nil, nil, 3)
   468  		require.Error(t, err)
   469  		require.EqualError(t, err, "too many functions (4) in a module")
   470  	})
   471  	t.Run("function, but no code", func(t *testing.T) {
   472  		m := Module{
   473  			TypeSection:     []FunctionType{v_v},
   474  			FunctionSection: []Index{0},
   475  			CodeSection:     nil,
   476  		}
   477  		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
   478  		require.Error(t, err)
   479  		require.EqualError(t, err, "code count (0) != function count (1)")
   480  	})
   481  	t.Run("function out of range of code", func(t *testing.T) {
   482  		m := Module{
   483  			TypeSection:     []FunctionType{v_v},
   484  			FunctionSection: []Index{1},
   485  			CodeSection:     []Code{{Body: []byte{OpcodeEnd}}},
   486  		}
   487  		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
   488  		require.Error(t, err)
   489  		require.EqualError(t, err, "invalid function[0]: type section index 1 out of range")
   490  	})
   491  	t.Run("invalid", func(t *testing.T) {
   492  		m := Module{
   493  			TypeSection:     []FunctionType{v_v},
   494  			FunctionSection: []Index{0},
   495  			CodeSection:     []Code{{Body: []byte{OpcodeF32Abs}}},
   496  		}
   497  		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
   498  		require.Error(t, err)
   499  		require.Contains(t, err.Error(), "invalid function[0]: cannot pop the 1st f32 operand")
   500  	})
   501  	t.Run("in- exported", func(t *testing.T) {
   502  		m := Module{
   503  			TypeSection:     []FunctionType{v_v},
   504  			FunctionSection: []Index{0},
   505  			CodeSection:     []Code{{Body: []byte{OpcodeF32Abs}}},
   506  			ExportSection:   []Export{{Name: "f1", Type: ExternTypeFunc, Index: 0}},
   507  		}
   508  		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
   509  		require.Error(t, err)
   510  		require.Contains(t, err.Error(), `invalid function[0] export["f1"]: cannot pop the 1st f32`)
   511  	})
   512  	t.Run("in- exported after import", func(t *testing.T) {
   513  		m := Module{
   514  			ImportFunctionCount: 1,
   515  			TypeSection:         []FunctionType{v_v},
   516  			ImportSection:       []Import{{Type: ExternTypeFunc}},
   517  			FunctionSection:     []Index{0},
   518  			CodeSection:         []Code{{Body: []byte{OpcodeF32Abs}}},
   519  			ExportSection:       []Export{{Name: "f1", Type: ExternTypeFunc, Index: 1}},
   520  		}
   521  		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
   522  		require.Error(t, err)
   523  		require.Contains(t, err.Error(), `invalid function[0] export["f1"]: cannot pop the 1st f32`)
   524  	})
   525  	t.Run("in- exported twice", func(t *testing.T) {
   526  		m := Module{
   527  			TypeSection:     []FunctionType{v_v},
   528  			FunctionSection: []Index{0},
   529  			CodeSection:     []Code{{Body: []byte{OpcodeF32Abs}}},
   530  			ExportSection: []Export{
   531  				{Name: "f1", Type: ExternTypeFunc, Index: 0},
   532  				{Name: "f2", Type: ExternTypeFunc, Index: 0},
   533  			},
   534  		}
   535  		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
   536  		require.Error(t, err)
   537  		require.Contains(t, err.Error(), `invalid function[0] export["f1","f2"]: cannot pop the 1st f32`)
   538  	})
   539  }
   540  
   541  func TestModule_validateMemory(t *testing.T) {
   542  	t.Run("active data segment exits but memory not declared", func(t *testing.T) {
   543  		m := Module{DataSection: []DataSegment{{OffsetExpression: ConstantExpression{}}}}
   544  		err := m.validateMemory(nil, nil, api.CoreFeaturesV1)
   545  		require.Error(t, err)
   546  		require.Contains(t, "unknown memory", err.Error())
   547  	})
   548  	t.Run("invalid const expr", func(t *testing.T) {
   549  		m := Module{DataSection: []DataSegment{{
   550  			OffsetExpression: ConstantExpression{
   551  				Opcode: OpcodeUnreachable, // Invalid!
   552  			},
   553  		}}}
   554  		err := m.validateMemory(&Memory{}, nil, api.CoreFeaturesV1)
   555  		require.EqualError(t, err, "calculate offset: invalid opcode for const expression: 0x0")
   556  	})
   557  	t.Run("ok", func(t *testing.T) {
   558  		m := Module{DataSection: []DataSegment{{
   559  			Init: []byte{0x1},
   560  			OffsetExpression: ConstantExpression{
   561  				Opcode: OpcodeI32Const,
   562  				Data:   leb128.EncodeInt32(1),
   563  			},
   564  		}}}
   565  		err := m.validateMemory(&Memory{}, nil, api.CoreFeaturesV1)
   566  		require.NoError(t, err)
   567  	})
   568  }
   569  
   570  func TestModule_validateImports(t *testing.T) {
   571  	tests := []struct {
   572  		name            string
   573  		enabledFeatures api.CoreFeatures
   574  		i               *Import
   575  		expectedErr     string
   576  	}{
   577  		{name: "empty import section"},
   578  		{
   579  			name:            "reject empty named module",
   580  			enabledFeatures: api.CoreFeaturesV1,
   581  			i:               &Import{Module: "", Name: "n", Type: ExternTypeFunc, DescFunc: 0},
   582  			expectedErr:     "import[0] has an empty module name",
   583  		},
   584  		{
   585  			name:            "func",
   586  			enabledFeatures: api.CoreFeaturesV1,
   587  			i:               &Import{Module: "m", Name: "n", Type: ExternTypeFunc, DescFunc: 0},
   588  		},
   589  		{
   590  			name:            "func type index out of range ",
   591  			enabledFeatures: api.CoreFeaturesV1,
   592  			i:               &Import{Module: "m", Name: "n", Type: ExternTypeFunc, DescFunc: 100},
   593  			expectedErr:     "invalid import[\"m\".\"n\"] function: type index out of range",
   594  		},
   595  		{
   596  			name:            "global var disabled",
   597  			enabledFeatures: api.CoreFeaturesV1.SetEnabled(api.CoreFeatureMutableGlobal, false),
   598  			i: &Import{
   599  				Module:     "m",
   600  				Name:       "n",
   601  				Type:       ExternTypeGlobal,
   602  				DescGlobal: GlobalType{ValType: ValueTypeI32, Mutable: true},
   603  			},
   604  			expectedErr: `invalid import["m"."n"] global: feature "mutable-global" is disabled`,
   605  		},
   606  		{
   607  			name:            "table",
   608  			enabledFeatures: api.CoreFeaturesV1,
   609  			i: &Import{
   610  				Module:    "m",
   611  				Name:      "n",
   612  				Type:      ExternTypeTable,
   613  				DescTable: Table{Min: 1},
   614  			},
   615  		},
   616  		{
   617  			name:            "memory",
   618  			enabledFeatures: api.CoreFeaturesV1,
   619  			i: &Import{
   620  				Module:  "m",
   621  				Name:    "n",
   622  				Type:    ExternTypeMemory,
   623  				DescMem: &Memory{Min: 1},
   624  			},
   625  		},
   626  	}
   627  
   628  	for _, tt := range tests {
   629  		tc := tt
   630  		t.Run(tc.name, func(t *testing.T) {
   631  			m := Module{TypeSection: []FunctionType{{}}}
   632  			if tc.i != nil {
   633  				m.ImportSection = []Import{*tc.i}
   634  			}
   635  			err := m.validateImports(tc.enabledFeatures)
   636  			if tc.expectedErr != "" {
   637  				require.EqualError(t, err, tc.expectedErr)
   638  			} else {
   639  				require.NoError(t, err)
   640  			}
   641  		})
   642  	}
   643  }
   644  
   645  func TestModule_validateExports(t *testing.T) {
   646  	tests := []struct {
   647  		name            string
   648  		enabledFeatures api.CoreFeatures
   649  		exportSection   []Export
   650  		functions       []Index
   651  		globals         []GlobalType
   652  		memory          *Memory
   653  		tables          []Table
   654  		expectedErr     string
   655  	}{
   656  		{name: "empty export section", exportSection: []Export{}},
   657  		{
   658  			name:            "func",
   659  			enabledFeatures: api.CoreFeaturesV1,
   660  			exportSection:   []Export{{Type: ExternTypeFunc, Index: 0}},
   661  			functions:       []Index{100 /* arbitrary type id*/},
   662  		},
   663  		{
   664  			name:            "func out of range",
   665  			enabledFeatures: api.CoreFeaturesV1,
   666  			exportSection:   []Export{{Type: ExternTypeFunc, Index: 1, Name: "e"}},
   667  			functions:       []Index{100 /* arbitrary type id*/},
   668  			expectedErr:     `unknown function for export["e"]`,
   669  		},
   670  		{
   671  			name:            "global const",
   672  			enabledFeatures: api.CoreFeaturesV1,
   673  			exportSection:   []Export{{Type: ExternTypeGlobal, Index: 0}},
   674  			globals:         []GlobalType{{ValType: ValueTypeI32}},
   675  		},
   676  		{
   677  			name:            "global var",
   678  			enabledFeatures: api.CoreFeaturesV1,
   679  			exportSection:   []Export{{Type: ExternTypeGlobal, Index: 0}},
   680  			globals:         []GlobalType{{ValType: ValueTypeI32, Mutable: true}},
   681  		},
   682  		{
   683  			name:            "global var disabled",
   684  			enabledFeatures: api.CoreFeaturesV1.SetEnabled(api.CoreFeatureMutableGlobal, false),
   685  			exportSection:   []Export{{Type: ExternTypeGlobal, Index: 0, Name: "e"}},
   686  			globals:         []GlobalType{{ValType: ValueTypeI32, Mutable: true}},
   687  			expectedErr:     `invalid export["e"] global[0]: feature "mutable-global" is disabled`,
   688  		},
   689  		{
   690  			name:            "global out of range",
   691  			enabledFeatures: api.CoreFeaturesV1,
   692  			exportSection:   []Export{{Type: ExternTypeGlobal, Index: 1, Name: "e"}},
   693  			globals:         []GlobalType{{}},
   694  			expectedErr:     `unknown global for export["e"]`,
   695  		},
   696  		{
   697  			name:            "table",
   698  			enabledFeatures: api.CoreFeaturesV1,
   699  			exportSection:   []Export{{Type: ExternTypeTable, Index: 0}},
   700  			tables:          []Table{{}},
   701  		},
   702  		{
   703  			name:            "multiple tables",
   704  			enabledFeatures: api.CoreFeaturesV1,
   705  			exportSection:   []Export{{Type: ExternTypeTable, Index: 0}, {Type: ExternTypeTable, Index: 1}, {Type: ExternTypeTable, Index: 2}},
   706  			tables:          []Table{{}, {}, {}},
   707  		},
   708  		{
   709  			name:            "table out of range",
   710  			enabledFeatures: api.CoreFeaturesV1,
   711  			exportSection:   []Export{{Type: ExternTypeTable, Index: 1, Name: "e"}},
   712  			tables:          []Table{},
   713  			expectedErr:     `table for export["e"] out of range`,
   714  		},
   715  		{
   716  			name:            "memory",
   717  			enabledFeatures: api.CoreFeaturesV1,
   718  			exportSection:   []Export{{Type: ExternTypeMemory, Index: 0}},
   719  			memory:          &Memory{},
   720  		},
   721  		{
   722  			name:            "memory out of range",
   723  			enabledFeatures: api.CoreFeaturesV1,
   724  			exportSection:   []Export{{Type: ExternTypeMemory, Index: 0, Name: "e"}},
   725  			tables:          []Table{},
   726  			expectedErr:     `memory for export["e"] out of range`,
   727  		},
   728  	}
   729  
   730  	for _, tt := range tests {
   731  		tc := tt
   732  		t.Run(tc.name, func(t *testing.T) {
   733  			m := Module{ExportSection: tc.exportSection}
   734  			err := m.validateExports(tc.enabledFeatures, tc.functions, tc.globals, tc.memory, tc.tables)
   735  			if tc.expectedErr != "" {
   736  				require.EqualError(t, err, tc.expectedErr)
   737  			} else {
   738  				require.NoError(t, err)
   739  			}
   740  		})
   741  	}
   742  }
   743  
   744  func TestModule_buildGlobals(t *testing.T) {
   745  	const localFuncRefInstructionIndex = uint32(0xffff)
   746  
   747  	minusOne := int32(-1)
   748  	m := &Module{
   749  		ImportGlobalCount: 2,
   750  		GlobalSection: []Global{
   751  			{
   752  				Type: GlobalType{Mutable: true, ValType: ValueTypeF64},
   753  				Init: ConstantExpression{
   754  					Opcode: OpcodeF64Const,
   755  					Data:   u64.LeBytes(api.EncodeF64(math.MaxFloat64)),
   756  				},
   757  			},
   758  			{
   759  				Type: GlobalType{Mutable: false, ValType: ValueTypeI32},
   760  				Init: ConstantExpression{
   761  					Opcode: OpcodeI32Const,
   762  					Data:   leb128.EncodeInt32(math.MaxInt32),
   763  				},
   764  			},
   765  			{
   766  				Type: GlobalType{Mutable: false, ValType: ValueTypeI32},
   767  				Init: ConstantExpression{
   768  					Opcode: OpcodeI32Const,
   769  					Data:   leb128.EncodeInt32(minusOne),
   770  				},
   771  			},
   772  			{
   773  				Type: GlobalType{Mutable: false, ValType: ValueTypeV128},
   774  				Init: ConstantExpression{
   775  					Opcode: OpcodeVecV128Const,
   776  					Data: []byte{
   777  						1, 0, 0, 0, 0, 0, 0, 0,
   778  						2, 0, 0, 0, 0, 0, 0, 0,
   779  					},
   780  				},
   781  			},
   782  			{
   783  				Type: GlobalType{Mutable: false, ValType: ValueTypeExternref},
   784  				Init: ConstantExpression{Opcode: OpcodeRefNull, Data: []byte{ValueTypeExternref}},
   785  			},
   786  			{
   787  				Type: GlobalType{Mutable: false, ValType: ValueTypeFuncref},
   788  				Init: ConstantExpression{Opcode: OpcodeRefNull, Data: []byte{ValueTypeFuncref}},
   789  			},
   790  			{
   791  				Type: GlobalType{Mutable: false, ValType: ValueTypeFuncref},
   792  				Init: ConstantExpression{Opcode: OpcodeRefFunc, Data: leb128.EncodeUint32(localFuncRefInstructionIndex)},
   793  			},
   794  			{
   795  				Type: GlobalType{Mutable: false, ValType: ValueTypeExternref},
   796  				Init: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0}},
   797  			},
   798  			{
   799  				Type: GlobalType{Mutable: false, ValType: ValueTypeFuncref},
   800  				Init: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{1}},
   801  			},
   802  		},
   803  	}
   804  
   805  	imported := []*GlobalInstance{
   806  		{Type: GlobalType{ValType: ValueTypeExternref}, Val: 0x54321},
   807  		{Type: GlobalType{ValType: ValueTypeFuncref}, Val: 0x12345},
   808  	}
   809  
   810  	mi := &ModuleInstance{
   811  		Globals: make([]*GlobalInstance, m.ImportGlobalCount+uint32(len(m.GlobalSection))),
   812  	}
   813  
   814  	mi.Globals[0], mi.Globals[1] = imported[0], imported[1]
   815  
   816  	mi.buildGlobals(m, func(funcIndex Index) Reference {
   817  		require.Equal(t, localFuncRefInstructionIndex, funcIndex)
   818  		return 0x99999
   819  	})
   820  	expectedGlobals := []*GlobalInstance{
   821  		imported[0], imported[1],
   822  		{Type: GlobalType{ValType: ValueTypeF64, Mutable: true}, Val: api.EncodeF64(math.MaxFloat64)},
   823  		{Type: GlobalType{ValType: ValueTypeI32, Mutable: false}, Val: uint64(int32(math.MaxInt32))},
   824  		// Higher bits are must be zeroed for i32 globals, not signed-extended. See #656.
   825  		{Type: GlobalType{ValType: ValueTypeI32, Mutable: false}, Val: uint64(uint32(minusOne))},
   826  		{Type: GlobalType{ValType: ValueTypeV128, Mutable: false}, Val: 0x1, ValHi: 0x2},
   827  		{Type: GlobalType{ValType: ValueTypeExternref, Mutable: false}, Val: 0},
   828  		{Type: GlobalType{ValType: ValueTypeFuncref, Mutable: false}, Val: 0},
   829  		{Type: GlobalType{ValType: ValueTypeFuncref, Mutable: false}, Val: 0x99999},
   830  		{Type: GlobalType{ValType: ValueTypeExternref, Mutable: false}, Val: 0x54321},
   831  		{Type: GlobalType{ValType: ValueTypeFuncref, Mutable: false}, Val: 0x12345},
   832  	}
   833  	require.Equal(t, expectedGlobals, mi.Globals)
   834  }
   835  
   836  func TestModule_buildMemoryInstance(t *testing.T) {
   837  	t.Run("nil", func(t *testing.T) {
   838  		m := ModuleInstance{}
   839  		m.buildMemory(&Module{})
   840  		require.Nil(t, m.MemoryInstance)
   841  	})
   842  	t.Run("non-nil", func(t *testing.T) {
   843  		min := uint32(1)
   844  		max := uint32(10)
   845  		mDef := MemoryDefinition{moduleName: "foo"}
   846  		m := ModuleInstance{}
   847  		m.buildMemory(&Module{
   848  			MemorySection:           &Memory{Min: min, Cap: min, Max: max},
   849  			MemoryDefinitionSection: []MemoryDefinition{mDef},
   850  		})
   851  		mem := m.MemoryInstance
   852  		require.Equal(t, min, mem.Min)
   853  		require.Equal(t, max, mem.Max)
   854  		require.Equal(t, &mDef, mem.definition)
   855  	})
   856  }
   857  
   858  func TestModule_validateDataCountSection(t *testing.T) {
   859  	t.Run("ok", func(t *testing.T) {
   860  		for _, m := range []*Module{
   861  			{
   862  				DataSection:      []DataSegment{},
   863  				DataCountSection: nil,
   864  			},
   865  			{
   866  				DataSection:      []DataSegment{{}, {}},
   867  				DataCountSection: nil,
   868  			},
   869  		} {
   870  			err := m.validateDataCountSection()
   871  			require.NoError(t, err)
   872  		}
   873  	})
   874  	t.Run("error", func(t *testing.T) {
   875  		count := uint32(1)
   876  		for _, m := range []*Module{
   877  			{
   878  				DataSection:      []DataSegment{},
   879  				DataCountSection: &count,
   880  			},
   881  			{
   882  				DataSection:      []DataSegment{{}, {}},
   883  				DataCountSection: &count,
   884  			},
   885  		} {
   886  			err := m.validateDataCountSection()
   887  			require.Error(t, err)
   888  		}
   889  	})
   890  }
   891  
   892  func TestModule_declaredFunctionIndexes(t *testing.T) {
   893  	tests := []struct {
   894  		name   string
   895  		mod    *Module
   896  		exp    map[Index]struct{}
   897  		expErr string
   898  	}{
   899  		{
   900  			name: "empty",
   901  			mod:  &Module{},
   902  			exp:  map[uint32]struct{}{},
   903  		},
   904  		{
   905  			name: "global",
   906  			mod: &Module{
   907  				ExportSection: []Export{
   908  					{Index: 10, Type: ExternTypeFunc},
   909  					{Index: 1000, Type: ExternTypeGlobal},
   910  				},
   911  			},
   912  			exp: map[uint32]struct{}{10: {}},
   913  		},
   914  		{
   915  			name: "export",
   916  			mod: &Module{
   917  				ExportSection: []Export{
   918  					{Index: 1000, Type: ExternTypeGlobal},
   919  					{Index: 10, Type: ExternTypeFunc},
   920  				},
   921  			},
   922  			exp: map[uint32]struct{}{10: {}},
   923  		},
   924  		{
   925  			name: "element",
   926  			mod: &Module{
   927  				ElementSection: []ElementSegment{
   928  					{
   929  						Mode: ElementModeActive,
   930  						Init: []Index{0, ElementInitNullReference, 5},
   931  					},
   932  					{
   933  						Mode: ElementModeDeclarative,
   934  						Init: []Index{1, ElementInitNullReference, 5},
   935  					},
   936  					{
   937  						Mode: ElementModePassive,
   938  						Init: []Index{5, 2, ElementInitNullReference, ElementInitNullReference},
   939  					},
   940  				},
   941  			},
   942  			exp: map[uint32]struct{}{0: {}, 1: {}, 2: {}, 5: {}},
   943  		},
   944  		{
   945  			name: "all",
   946  			mod: &Module{
   947  				ExportSection: []Export{
   948  					{Index: 10, Type: ExternTypeGlobal},
   949  					{Index: 1000, Type: ExternTypeFunc},
   950  				},
   951  				GlobalSection: []Global{
   952  					{
   953  						Init: ConstantExpression{
   954  							Opcode: OpcodeI32Const, // not funcref.
   955  							Data:   leb128.EncodeInt32(-1),
   956  						},
   957  					},
   958  					{
   959  						Init: ConstantExpression{
   960  							Opcode: OpcodeRefFunc,
   961  							Data:   leb128.EncodeInt32(123),
   962  						},
   963  					},
   964  				},
   965  				ElementSection: []ElementSegment{
   966  					{
   967  						Mode: ElementModeActive,
   968  						Init: []Index{0, ElementInitNullReference, 5},
   969  					},
   970  					{
   971  						Mode: ElementModeDeclarative,
   972  						Init: []Index{1, ElementInitNullReference, 5},
   973  					},
   974  					{
   975  						Mode: ElementModePassive,
   976  						Init: []Index{5, 2, ElementInitNullReference, ElementInitNullReference},
   977  					},
   978  				},
   979  			},
   980  			exp: map[uint32]struct{}{0: {}, 1: {}, 2: {}, 5: {}, 123: {}, 1000: {}},
   981  		},
   982  		{
   983  			mod: &Module{
   984  				GlobalSection: []Global{
   985  					{
   986  						Init: ConstantExpression{
   987  							Opcode: OpcodeRefFunc,
   988  							Data:   nil,
   989  						},
   990  					},
   991  				},
   992  			},
   993  			name:   "invalid global",
   994  			expErr: `global[0] failed to initialize: EOF`,
   995  		},
   996  	}
   997  
   998  	for _, tt := range tests {
   999  		tc := tt
  1000  		t.Run(tc.name, func(t *testing.T) {
  1001  			actual, err := tc.mod.declaredFunctionIndexes()
  1002  			if tc.expErr != "" {
  1003  				require.EqualError(t, err, tc.expErr)
  1004  			} else {
  1005  				require.NoError(t, err)
  1006  				require.Equal(t, tc.exp, actual)
  1007  			}
  1008  		})
  1009  	}
  1010  }
  1011  
  1012  func TestModule_AssignModuleID(t *testing.T) {
  1013  	getID := func(bin []byte, withListener, withEnsureTermination bool) ModuleID {
  1014  		m := Module{}
  1015  		m.AssignModuleID(bin, withListener, withEnsureTermination)
  1016  		return m.ID
  1017  	}
  1018  
  1019  	// Ensures that different args always produce the different IDs.
  1020  	exists := map[ModuleID]struct{}{}
  1021  	for _, tc := range []struct {
  1022  		bin                                 []byte
  1023  		withListener, withEnsureTermination bool
  1024  	}{
  1025  		{bin: []byte{1, 2, 3}, withListener: false, withEnsureTermination: false},
  1026  		{bin: []byte{1, 2, 3}, withListener: false, withEnsureTermination: true},
  1027  		{bin: []byte{1, 2, 3}, withListener: true, withEnsureTermination: false},
  1028  		{bin: []byte{1, 2, 3}, withListener: true, withEnsureTermination: true},
  1029  		{bin: []byte{1, 2, 3, 4}, withListener: false, withEnsureTermination: false},
  1030  		{bin: []byte{1, 2, 3, 4}, withListener: false, withEnsureTermination: true},
  1031  		{bin: []byte{1, 2, 3, 4}, withListener: true, withEnsureTermination: false},
  1032  		{bin: []byte{1, 2, 3, 4}, withListener: true, withEnsureTermination: true},
  1033  	} {
  1034  		id := getID(tc.bin, tc.withListener, tc.withEnsureTermination)
  1035  		_, exist := exists[id]
  1036  		require.False(t, exist)
  1037  		exists[id] = struct{}{}
  1038  	}
  1039  }