github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/wasm/module_test.go (about)

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