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

     1  package wasm
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"math"
     8  	"strconv"
     9  	"testing"
    10  
    11  	"github.com/tetratelabs/wazero/api"
    12  	"github.com/tetratelabs/wazero/experimental"
    13  	"github.com/tetratelabs/wazero/internal/internalapi"
    14  	"github.com/tetratelabs/wazero/internal/leb128"
    15  	"github.com/tetratelabs/wazero/internal/sys"
    16  	"github.com/tetratelabs/wazero/internal/testing/hammer"
    17  	"github.com/tetratelabs/wazero/internal/testing/require"
    18  	"github.com/tetratelabs/wazero/internal/u64"
    19  )
    20  
    21  func TestModuleInstance_Memory(t *testing.T) {
    22  	tests := []struct {
    23  		name        string
    24  		input       *Module
    25  		expected    bool
    26  		expectedLen uint32
    27  	}{
    28  		{
    29  			name:  "no memory",
    30  			input: &Module{},
    31  		},
    32  		{
    33  			name: "memory not exported, one page",
    34  			input: &Module{
    35  				MemorySection:           &Memory{Min: 1, Cap: 1},
    36  				MemoryDefinitionSection: []MemoryDefinition{{}},
    37  			},
    38  		},
    39  		{
    40  			name: "memory exported, different name",
    41  			input: &Module{
    42  				MemorySection:           &Memory{Min: 1, Cap: 1},
    43  				MemoryDefinitionSection: []MemoryDefinition{{}},
    44  				ExportSection:           []Export{{Type: ExternTypeMemory, Name: "momory", Index: 0}},
    45  			},
    46  		},
    47  		{
    48  			name: "memory exported, but zero length",
    49  			input: &Module{
    50  				MemorySection:           &Memory{},
    51  				MemoryDefinitionSection: []MemoryDefinition{{}},
    52  				Exports:                 map[string]*Export{"memory": {Type: ExternTypeMemory, Name: "memory"}},
    53  			},
    54  			expected: true,
    55  		},
    56  		{
    57  			name: "memory exported, one page",
    58  			input: &Module{
    59  				MemorySection:           &Memory{Min: 1, Cap: 1},
    60  				MemoryDefinitionSection: []MemoryDefinition{{}},
    61  				Exports:                 map[string]*Export{"memory": {Type: ExternTypeMemory, Name: "memory"}},
    62  			},
    63  			expected:    true,
    64  			expectedLen: 65536,
    65  		},
    66  		{
    67  			name: "memory exported, two pages",
    68  			input: &Module{
    69  				MemorySection:           &Memory{Min: 2, Cap: 2},
    70  				MemoryDefinitionSection: []MemoryDefinition{{}},
    71  				Exports:                 map[string]*Export{"memory": {Type: ExternTypeMemory, Name: "memory"}},
    72  			},
    73  			expected:    true,
    74  			expectedLen: 65536 * 2,
    75  		},
    76  	}
    77  
    78  	for _, tt := range tests {
    79  		tc := tt
    80  
    81  		t.Run(tc.name, func(t *testing.T) {
    82  			s := newStore()
    83  
    84  			instance, err := s.Instantiate(testCtx, tc.input, "test", nil, nil)
    85  			require.NoError(t, err)
    86  
    87  			mem := instance.ExportedMemory("memory")
    88  			if tc.expected {
    89  				require.Equal(t, tc.expectedLen, mem.Size())
    90  			} else {
    91  				require.Nil(t, mem)
    92  			}
    93  		})
    94  	}
    95  }
    96  
    97  func TestStore_Instantiate(t *testing.T) {
    98  	s := newStore()
    99  	m, err := NewHostModule(
   100  		"foo",
   101  		[]string{"fn"},
   102  		map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: func() {}}}},
   103  		api.CoreFeaturesV1,
   104  	)
   105  	require.NoError(t, err)
   106  
   107  	sysCtx := sys.DefaultContext(nil)
   108  	mod, err := s.Instantiate(testCtx, m, "bar", sysCtx, []FunctionTypeID{0})
   109  	require.NoError(t, err)
   110  	defer mod.Close(testCtx)
   111  
   112  	t.Run("ModuleInstance defaults", func(t *testing.T) {
   113  		require.Equal(t, s.nameToModule["bar"], mod)
   114  		require.Equal(t, s.nameToModule["bar"].MemoryInstance, mod.MemoryInstance)
   115  		require.Equal(t, s, mod.s)
   116  		require.Equal(t, sysCtx, mod.Sys)
   117  	})
   118  }
   119  
   120  func TestStore_CloseWithExitCode(t *testing.T) {
   121  	const importedModuleName = "imported"
   122  	const importingModuleName = "test"
   123  
   124  	tests := []struct {
   125  		name       string
   126  		testClosed bool
   127  	}{
   128  		{
   129  			name:       "nothing closed",
   130  			testClosed: false,
   131  		},
   132  		{
   133  			name:       "partially closed",
   134  			testClosed: true,
   135  		},
   136  	}
   137  
   138  	for _, tt := range tests {
   139  		tc := tt
   140  		t.Run(tc.name, func(t *testing.T) {
   141  			s := newStore()
   142  
   143  			_, err := s.Instantiate(testCtx, &Module{
   144  				TypeSection:               []FunctionType{v_v},
   145  				FunctionSection:           []uint32{0},
   146  				CodeSection:               []Code{{Body: []byte{OpcodeEnd}}},
   147  				Exports:                   map[string]*Export{"fn": {Type: ExternTypeFunc, Name: "fn"}},
   148  				FunctionDefinitionSection: []FunctionDefinition{{Functype: &v_v}},
   149  			}, importedModuleName, nil, []FunctionTypeID{0})
   150  			require.NoError(t, err)
   151  
   152  			m2, err := s.Instantiate(testCtx, &Module{
   153  				ImportFunctionCount:     1,
   154  				TypeSection:             []FunctionType{v_v},
   155  				ImportSection:           []Import{{Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0}},
   156  				MemorySection:           &Memory{Min: 1, Cap: 1},
   157  				MemoryDefinitionSection: []MemoryDefinition{{}},
   158  				GlobalSection:           []Global{{Type: GlobalType{}, Init: ConstantExpression{Opcode: OpcodeI32Const, Data: const1}}},
   159  				TableSection:            []Table{{Min: 10}},
   160  			}, importingModuleName, nil, []FunctionTypeID{0})
   161  			require.NoError(t, err)
   162  
   163  			if tc.testClosed {
   164  				err = m2.CloseWithExitCode(testCtx, 2)
   165  				require.NoError(t, err)
   166  			}
   167  
   168  			err = s.CloseWithExitCode(testCtx, 2)
   169  			require.NoError(t, err)
   170  
   171  			// If Store.CloseWithExitCode was dispatched properly, modules should be empty
   172  			require.Nil(t, s.moduleList)
   173  
   174  			// Store state zeroed
   175  			require.Zero(t, len(s.typeIDs))
   176  		})
   177  	}
   178  }
   179  
   180  func TestStore_hammer(t *testing.T) {
   181  	const importedModuleName = "imported"
   182  
   183  	m, err := NewHostModule(
   184  		importedModuleName,
   185  		[]string{"fn"},
   186  		map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: func() {}}}},
   187  		api.CoreFeaturesV1,
   188  	)
   189  	require.NoError(t, err)
   190  
   191  	s := newStore()
   192  	imported, err := s.Instantiate(testCtx, m, importedModuleName, nil, []FunctionTypeID{0})
   193  	require.NoError(t, err)
   194  
   195  	_, ok := s.nameToModule[imported.Name()]
   196  	require.True(t, ok)
   197  
   198  	importingModule := &Module{
   199  		ImportFunctionCount:     1,
   200  		TypeSection:             []FunctionType{v_v},
   201  		FunctionSection:         []uint32{0},
   202  		CodeSection:             []Code{{Body: []byte{OpcodeEnd}}},
   203  		MemorySection:           &Memory{Min: 1, Cap: 1},
   204  		MemoryDefinitionSection: []MemoryDefinition{{}},
   205  		GlobalSection: []Global{{
   206  			Type: GlobalType{ValType: ValueTypeI32},
   207  			Init: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(1)},
   208  		}},
   209  		TableSection: []Table{{Min: 10}},
   210  		ImportSection: []Import{
   211  			{Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0},
   212  		},
   213  	}
   214  
   215  	// Concurrent instantiate, close should test if locks work on the ns. If they don't, we should see leaked modules
   216  	// after all of these complete, or an error raised.
   217  	P := 8               // max count of goroutines
   218  	N := 1000            // work per goroutine
   219  	if testing.Short() { // Adjust down if `-test.short`
   220  		P = 4
   221  		N = 100
   222  	}
   223  	hammer.NewHammer(t, P, N).Run(func(name string) {
   224  		mod, instantiateErr := s.Instantiate(testCtx, importingModule, name, sys.DefaultContext(nil), []FunctionTypeID{0})
   225  		require.NoError(t, instantiateErr)
   226  		require.NoError(t, mod.Close(testCtx))
   227  	}, nil)
   228  	if t.Failed() {
   229  		return // At least one test failed, so return now.
   230  	}
   231  
   232  	// Close the imported module.
   233  	require.NoError(t, imported.Close(testCtx))
   234  
   235  	// All instances are freed.
   236  	require.Nil(t, s.moduleList)
   237  }
   238  
   239  func TestStore_hammer_close(t *testing.T) {
   240  	const importedModuleName = "imported"
   241  
   242  	m, err := NewHostModule(
   243  		importedModuleName,
   244  		[]string{"fn"},
   245  		map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: func() {}}}},
   246  		api.CoreFeaturesV1,
   247  	)
   248  	require.NoError(t, err)
   249  
   250  	s := newStore()
   251  	imported, err := s.Instantiate(testCtx, m, importedModuleName, nil, []FunctionTypeID{0})
   252  	require.NoError(t, err)
   253  
   254  	_, ok := s.nameToModule[imported.Name()]
   255  	require.True(t, ok)
   256  
   257  	importingModule := &Module{
   258  		ImportFunctionCount:     1,
   259  		TypeSection:             []FunctionType{v_v},
   260  		FunctionSection:         []uint32{0},
   261  		CodeSection:             []Code{{Body: []byte{OpcodeEnd}}},
   262  		MemorySection:           &Memory{Min: 1, Cap: 1},
   263  		MemoryDefinitionSection: []MemoryDefinition{{}},
   264  		GlobalSection: []Global{{
   265  			Type: GlobalType{ValType: ValueTypeI32},
   266  			Init: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(1)},
   267  		}},
   268  		TableSection: []Table{{Min: 10}},
   269  		ImportSection: []Import{
   270  			{Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0},
   271  		},
   272  	}
   273  
   274  	const instCount = 10000
   275  	instances := make([]api.Module, instCount)
   276  	for i := 0; i < instCount; i++ {
   277  		mod, instantiateErr := s.Instantiate(testCtx, importingModule, strconv.Itoa(i), sys.DefaultContext(nil), []FunctionTypeID{0})
   278  		require.NoError(t, instantiateErr)
   279  		instances[i] = mod
   280  	}
   281  
   282  	hammer.NewHammer(t, 100, 2).Run(func(name string) {
   283  		for i := 0; i < instCount; i++ {
   284  			if i == instCount/2 {
   285  				// Close store concurrently as well.
   286  				err := s.CloseWithExitCode(testCtx, 0)
   287  				require.NoError(t, err)
   288  			}
   289  			err := instances[i].CloseWithExitCode(testCtx, 0)
   290  			require.NoError(t, err)
   291  		}
   292  		require.NoError(t, err)
   293  	}, nil)
   294  	if t.Failed() {
   295  		return // At least one test failed, so return now.
   296  	}
   297  
   298  	// All instances are freed.
   299  	require.Nil(t, s.moduleList)
   300  }
   301  
   302  func TestStore_Instantiate_Errors(t *testing.T) {
   303  	const importedModuleName = "imported"
   304  	const importingModuleName = "test"
   305  
   306  	m, err := NewHostModule(
   307  		importedModuleName,
   308  		[]string{"fn"},
   309  		map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: func() {}}}},
   310  		api.CoreFeaturesV1,
   311  	)
   312  	require.NoError(t, err)
   313  
   314  	t.Run("Fails if module name already in use", func(t *testing.T) {
   315  		s := newStore()
   316  		_, err = s.Instantiate(testCtx, m, importedModuleName, nil, []FunctionTypeID{0})
   317  		require.NoError(t, err)
   318  
   319  		// Trying to register it again should fail
   320  		_, err = s.Instantiate(testCtx, m, importedModuleName, nil, []FunctionTypeID{0})
   321  		require.EqualError(t, err, "module[imported] has already been instantiated")
   322  	})
   323  
   324  	t.Run("fail resolve import", func(t *testing.T) {
   325  		s := newStore()
   326  		_, err = s.Instantiate(testCtx, m, importedModuleName, nil, []FunctionTypeID{0})
   327  		require.NoError(t, err)
   328  
   329  		hm := s.nameToModule[importedModuleName]
   330  		require.NotNil(t, hm)
   331  
   332  		_, err = s.Instantiate(testCtx, &Module{
   333  			TypeSection: []FunctionType{v_v},
   334  			ImportSection: []Import{
   335  				// The first import resolve succeeds -> increment hm.dependentCount.
   336  				{Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0},
   337  				// But the second one tries to import uninitialized-module ->
   338  				{Type: ExternTypeFunc, Module: "non-exist", Name: "fn", DescFunc: 0},
   339  			},
   340  			ImportPerModule: map[string][]*Import{
   341  				importedModuleName: {{Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0}},
   342  				"non-exist":        {{Name: "fn", DescFunc: 0}},
   343  			},
   344  		}, importingModuleName, nil, nil)
   345  		require.EqualError(t, err, "module[non-exist] not instantiated")
   346  	})
   347  
   348  	t.Run("creating engine failed", func(t *testing.T) {
   349  		s := newStore()
   350  
   351  		_, err = s.Instantiate(testCtx, m, importedModuleName, nil, []FunctionTypeID{0})
   352  		require.NoError(t, err)
   353  
   354  		hm := s.nameToModule[importedModuleName]
   355  		require.NotNil(t, hm)
   356  
   357  		engine := s.Engine.(*mockEngine)
   358  		engine.shouldCompileFail = true
   359  
   360  		importingModule := &Module{
   361  			ImportFunctionCount: 1,
   362  			TypeSection:         []FunctionType{v_v},
   363  			FunctionSection:     []uint32{0, 0},
   364  			CodeSection: []Code{
   365  				{Body: []byte{OpcodeEnd}},
   366  				{Body: []byte{OpcodeEnd}},
   367  			},
   368  			ImportSection: []Import{
   369  				{Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0},
   370  			},
   371  		}
   372  
   373  		_, err = s.Instantiate(testCtx, importingModule, importingModuleName, nil, []FunctionTypeID{0})
   374  		require.EqualError(t, err, "some engine creation error")
   375  	})
   376  
   377  	t.Run("start func failed", func(t *testing.T) {
   378  		s := newStore()
   379  		engine := s.Engine.(*mockEngine)
   380  		engine.callFailIndex = 1
   381  
   382  		_, err = s.Instantiate(testCtx, m, importedModuleName, nil, []FunctionTypeID{0})
   383  		require.NoError(t, err)
   384  
   385  		hm := s.nameToModule[importedModuleName]
   386  		require.NotNil(t, hm)
   387  
   388  		startFuncIndex := uint32(1)
   389  		importingModule := &Module{
   390  			ImportFunctionCount: 1,
   391  			TypeSection:         []FunctionType{v_v},
   392  			FunctionSection:     []uint32{0},
   393  			CodeSection:         []Code{{Body: []byte{OpcodeEnd}}},
   394  			StartSection:        &startFuncIndex,
   395  			ImportSection: []Import{
   396  				{Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0},
   397  			},
   398  		}
   399  
   400  		_, err = s.Instantiate(testCtx, importingModule, importingModuleName, nil, []FunctionTypeID{0})
   401  		require.EqualError(t, err, "start function[1] failed: call failed")
   402  	})
   403  }
   404  
   405  type mockEngine struct {
   406  	shouldCompileFail bool
   407  	callFailIndex     int
   408  }
   409  
   410  type mockModuleEngine struct {
   411  	name                 string
   412  	callFailIndex        int
   413  	functionRefs         map[Index]Reference
   414  	resolveImportsCalled map[Index]Index
   415  }
   416  
   417  type mockCallEngine struct {
   418  	internalapi.WazeroOnlyType
   419  	index         Index
   420  	callFailIndex int
   421  }
   422  
   423  func newStore() *Store {
   424  	return NewStore(api.CoreFeaturesV1, &mockEngine{shouldCompileFail: false, callFailIndex: -1})
   425  }
   426  
   427  // CompileModule implements the same method as documented on wasm.Engine.
   428  func (e *mockEngine) Close() error {
   429  	return nil
   430  }
   431  
   432  // CompileModule implements the same method as documented on wasm.Engine.
   433  func (e *mockEngine) CompileModule(context.Context, *Module, []experimental.FunctionListener, bool) error {
   434  	return nil
   435  }
   436  
   437  // LookupFunction implements the same method as documented on wasm.Engine.
   438  func (e *mockModuleEngine) LookupFunction(*TableInstance, FunctionTypeID, Index) (api.Function, error) {
   439  	return nil, nil
   440  }
   441  
   442  // CompiledModuleCount implements the same method as documented on wasm.Engine.
   443  func (e *mockEngine) CompiledModuleCount() uint32 { return 0 }
   444  
   445  // DeleteCompiledModule implements the same method as documented on wasm.Engine.
   446  func (e *mockEngine) DeleteCompiledModule(*Module) {}
   447  
   448  // NewModuleEngine implements the same method as documented on wasm.Engine.
   449  func (e *mockEngine) NewModuleEngine(_ *Module, _ *ModuleInstance) (ModuleEngine, error) {
   450  	if e.shouldCompileFail {
   451  		return nil, fmt.Errorf("some engine creation error")
   452  	}
   453  	return &mockModuleEngine{callFailIndex: e.callFailIndex, resolveImportsCalled: map[Index]Index{}}, nil
   454  }
   455  
   456  // FunctionInstanceReference implements the same method as documented on wasm.ModuleEngine.
   457  func (e *mockModuleEngine) FunctionInstanceReference(i Index) Reference {
   458  	return e.functionRefs[i]
   459  }
   460  
   461  // ResolveImportedFunction implements the same method as documented on wasm.ModuleEngine.
   462  func (e *mockModuleEngine) ResolveImportedFunction(index, importedIndex Index, _ ModuleEngine) {
   463  	e.resolveImportsCalled[index] = importedIndex
   464  }
   465  
   466  // NewFunction implements the same method as documented on wasm.ModuleEngine.
   467  func (e *mockModuleEngine) NewFunction(index Index) api.Function {
   468  	return &mockCallEngine{index: index, callFailIndex: e.callFailIndex}
   469  }
   470  
   471  // InitializeFuncrefGlobals implements the same method as documented on wasm.ModuleEngine.
   472  func (e *mockModuleEngine) InitializeFuncrefGlobals(globals []*GlobalInstance) {}
   473  
   474  // Name implements the same method as documented on wasm.ModuleEngine.
   475  func (e *mockModuleEngine) Name() string {
   476  	return e.name
   477  }
   478  
   479  // Close implements the same method as documented on wasm.ModuleEngine.
   480  func (e *mockModuleEngine) Close(context.Context) {
   481  }
   482  
   483  // Call implements the same method as documented on api.Function.
   484  func (ce *mockCallEngine) Definition() api.FunctionDefinition { return nil }
   485  
   486  // Call implements the same method as documented on api.Function.
   487  func (ce *mockCallEngine) Call(ctx context.Context, _ ...uint64) (results []uint64, err error) {
   488  	return nil, ce.CallWithStack(ctx, nil)
   489  }
   490  
   491  // CallWithStack implements the same method as documented on api.Function.
   492  func (ce *mockCallEngine) CallWithStack(_ context.Context, _ []uint64) error {
   493  	if ce.callFailIndex >= 0 && ce.index == Index(ce.callFailIndex) {
   494  		return errors.New("call failed")
   495  	}
   496  	return nil
   497  }
   498  
   499  func TestStore_getFunctionTypeID(t *testing.T) {
   500  	t.Run("too many functions", func(t *testing.T) {
   501  		s := newStore()
   502  		const max = 10
   503  		s.functionMaxTypes = max
   504  		s.typeIDs = make(map[string]FunctionTypeID)
   505  		for i := 0; i < max; i++ {
   506  			s.typeIDs[strconv.Itoa(i)] = 0
   507  		}
   508  		_, err := s.GetFunctionTypeID(&FunctionType{})
   509  		require.Error(t, err)
   510  	})
   511  	t.Run("ok", func(t *testing.T) {
   512  		tests := []FunctionType{
   513  			{Params: []ValueType{}},
   514  			{Params: []ValueType{ValueTypeF32}},
   515  			{Results: []ValueType{ValueTypeF64}},
   516  			{Params: []ValueType{ValueTypeI32}, Results: []ValueType{ValueTypeI64}},
   517  		}
   518  
   519  		for _, tt := range tests {
   520  			tc := tt
   521  			t.Run(tc.String(), func(t *testing.T) {
   522  				s := newStore()
   523  				actual, err := s.GetFunctionTypeID(&tc)
   524  				require.NoError(t, err)
   525  
   526  				expectedTypeID, ok := s.typeIDs[tc.String()]
   527  				require.True(t, ok)
   528  				require.Equal(t, expectedTypeID, actual)
   529  			})
   530  		}
   531  	})
   532  }
   533  
   534  func TestGlobalInstance_initialize(t *testing.T) {
   535  	t.Run("basic type const expr", func(t *testing.T) {
   536  		for _, vt := range []ValueType{ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64} {
   537  			t.Run(ValueTypeName(vt), func(t *testing.T) {
   538  				g := &GlobalInstance{Type: GlobalType{ValType: vt}}
   539  				expr := &ConstantExpression{}
   540  				switch vt {
   541  				case ValueTypeI32:
   542  					expr.Data = []byte{1}
   543  					expr.Opcode = OpcodeI32Const
   544  				case ValueTypeI64:
   545  					expr.Data = []byte{2}
   546  					expr.Opcode = OpcodeI64Const
   547  				case ValueTypeF32:
   548  					expr.Data = u64.LeBytes(api.EncodeF32(math.MaxFloat32))
   549  					expr.Opcode = OpcodeF32Const
   550  				case ValueTypeF64:
   551  					expr.Data = u64.LeBytes(api.EncodeF64(math.MaxFloat64))
   552  					expr.Opcode = OpcodeF64Const
   553  				}
   554  
   555  				g.initialize(nil, expr, nil)
   556  
   557  				switch vt {
   558  				case ValueTypeI32:
   559  					require.Equal(t, int32(1), int32(g.Val))
   560  				case ValueTypeI64:
   561  					require.Equal(t, int64(2), int64(g.Val))
   562  				case ValueTypeF32:
   563  					require.Equal(t, float32(math.MaxFloat32), math.Float32frombits(uint32(g.Val)))
   564  				case ValueTypeF64:
   565  					require.Equal(t, math.MaxFloat64, math.Float64frombits(g.Val))
   566  				}
   567  			})
   568  		}
   569  	})
   570  	t.Run("ref.null", func(t *testing.T) {
   571  		tests := []struct {
   572  			name string
   573  			expr *ConstantExpression
   574  		}{
   575  			{
   576  				name: "ref.null (externref)",
   577  				expr: &ConstantExpression{
   578  					Opcode: OpcodeRefNull,
   579  					Data:   []byte{RefTypeExternref},
   580  				},
   581  			},
   582  			{
   583  				name: "ref.null (funcref)",
   584  				expr: &ConstantExpression{
   585  					Opcode: OpcodeRefNull,
   586  					Data:   []byte{RefTypeFuncref},
   587  				},
   588  			},
   589  		}
   590  
   591  		for _, tt := range tests {
   592  			tc := tt
   593  			t.Run(tc.name, func(t *testing.T) {
   594  				g := GlobalInstance{}
   595  				g.Type.ValType = tc.expr.Data[0]
   596  				g.initialize(nil, tc.expr, nil)
   597  				require.Equal(t, uint64(0), g.Val)
   598  			})
   599  		}
   600  	})
   601  	t.Run("ref.func", func(t *testing.T) {
   602  		g := GlobalInstance{Type: GlobalType{ValType: RefTypeFuncref}}
   603  		g.initialize(nil,
   604  			&ConstantExpression{Opcode: OpcodeRefFunc, Data: []byte{1}},
   605  			func(funcIndex Index) Reference {
   606  				require.Equal(t, Index(1), funcIndex)
   607  				return 0xdeadbeaf
   608  			},
   609  		)
   610  		require.Equal(t, uint64(0xdeadbeaf), g.Val)
   611  	})
   612  	t.Run("global expr", func(t *testing.T) {
   613  		tests := []struct {
   614  			valueType  ValueType
   615  			val, valHi uint64
   616  		}{
   617  			{valueType: ValueTypeI32, val: 10},
   618  			{valueType: ValueTypeI64, val: 20},
   619  			{valueType: ValueTypeF32, val: uint64(math.Float32bits(634634432.12311))},
   620  			{valueType: ValueTypeF64, val: math.Float64bits(1.12312311)},
   621  			{valueType: ValueTypeV128, val: 0x1, valHi: 0x2},
   622  			{valueType: ValueTypeExternref, val: 0x12345},
   623  			{valueType: ValueTypeFuncref, val: 0x54321},
   624  		}
   625  
   626  		for _, tt := range tests {
   627  			tc := tt
   628  			t.Run(ValueTypeName(tc.valueType), func(t *testing.T) {
   629  				// The index specified in Data equals zero.
   630  				expr := &ConstantExpression{Data: []byte{0}, Opcode: OpcodeGlobalGet}
   631  				globals := []*GlobalInstance{{Val: tc.val, ValHi: tc.valHi, Type: GlobalType{ValType: tc.valueType}}}
   632  
   633  				g := &GlobalInstance{Type: GlobalType{ValType: tc.valueType}}
   634  				g.initialize(globals, expr, nil)
   635  
   636  				switch tc.valueType {
   637  				case ValueTypeI32:
   638  					require.Equal(t, int32(tc.val), int32(g.Val))
   639  				case ValueTypeI64:
   640  					require.Equal(t, int64(tc.val), int64(g.Val))
   641  				case ValueTypeF32:
   642  					require.Equal(t, tc.val, g.Val)
   643  				case ValueTypeF64:
   644  					require.Equal(t, tc.val, g.Val)
   645  				case ValueTypeV128:
   646  					require.Equal(t, uint64(0x1), g.Val)
   647  					require.Equal(t, uint64(0x2), g.ValHi)
   648  				case ValueTypeFuncref, ValueTypeExternref:
   649  					require.Equal(t, tc.val, g.Val)
   650  				}
   651  			})
   652  		}
   653  	})
   654  
   655  	t.Run("vector", func(t *testing.T) {
   656  		expr := &ConstantExpression{Data: []byte{
   657  			1, 0, 0, 0, 0, 0, 0, 0,
   658  			2, 0, 0, 0, 0, 0, 0, 0,
   659  		}, Opcode: OpcodeVecV128Const}
   660  		g := GlobalInstance{Type: GlobalType{ValType: ValueTypeV128}}
   661  		g.initialize(nil, expr, nil)
   662  		require.Equal(t, uint64(0x1), g.Val)
   663  		require.Equal(t, uint64(0x2), g.ValHi)
   664  	})
   665  }
   666  
   667  func Test_resolveImports(t *testing.T) {
   668  	const moduleName = "test"
   669  	const name = "target"
   670  
   671  	t.Run("module not instantiated", func(t *testing.T) {
   672  		m := &ModuleInstance{s: newStore()}
   673  		err := m.resolveImports(&Module{ImportPerModule: map[string][]*Import{"unknown": {{}}}})
   674  		require.EqualError(t, err, "module[unknown] not instantiated")
   675  	})
   676  	t.Run("export instance not found", func(t *testing.T) {
   677  		m := &ModuleInstance{s: newStore()}
   678  		m.s.nameToModule[moduleName] = &ModuleInstance{Exports: map[string]*Export{}, ModuleName: moduleName}
   679  		err := m.resolveImports(&Module{ImportPerModule: map[string][]*Import{moduleName: {{Name: "unknown"}}}})
   680  		require.EqualError(t, err, "\"unknown\" is not exported in module \"test\"")
   681  	})
   682  	t.Run("func", func(t *testing.T) {
   683  		t.Run("ok", func(t *testing.T) {
   684  			s := newStore()
   685  			s.nameToModule[moduleName] = &ModuleInstance{
   686  				Exports: map[string]*Export{
   687  					name: {Type: ExternTypeFunc, Index: 2},
   688  					"":   {Type: ExternTypeFunc, Index: 4},
   689  				},
   690  				ModuleName: moduleName,
   691  				Source: &Module{
   692  					FunctionSection: []Index{0, 0, 1, 0, 0},
   693  					TypeSection: []FunctionType{
   694  						{Params: []ValueType{ExternTypeFunc}},
   695  						{Params: []ValueType{i32}, Results: []ValueType{ValueTypeV128}},
   696  					},
   697  				},
   698  			}
   699  
   700  			module := &Module{
   701  				TypeSection: []FunctionType{
   702  					{Params: []ValueType{i32}, Results: []ValueType{ValueTypeV128}},
   703  					{Params: []ValueType{ExternTypeFunc}},
   704  				},
   705  				ImportFunctionCount: 2,
   706  				ImportPerModule: map[string][]*Import{
   707  					moduleName: {
   708  						{Module: moduleName, Name: name, Type: ExternTypeFunc, DescFunc: 0, IndexPerType: 0},
   709  						{Module: moduleName, Name: "", Type: ExternTypeFunc, DescFunc: 1, IndexPerType: 1},
   710  					},
   711  				},
   712  			}
   713  
   714  			m := &ModuleInstance{Engine: &mockModuleEngine{resolveImportsCalled: map[Index]Index{}}, s: s, Source: module}
   715  			err := m.resolveImports(module)
   716  			require.NoError(t, err)
   717  
   718  			me := m.Engine.(*mockModuleEngine)
   719  			require.Equal(t, me.resolveImportsCalled[0], Index(2))
   720  			require.Equal(t, me.resolveImportsCalled[1], Index(4))
   721  		})
   722  		t.Run("signature mismatch", func(t *testing.T) {
   723  			s := newStore()
   724  			s.nameToModule[moduleName] = &ModuleInstance{
   725  				Exports: map[string]*Export{
   726  					name: {Type: ExternTypeFunc, Index: 0},
   727  				},
   728  				ModuleName: moduleName,
   729  				TypeIDs:    []FunctionTypeID{123435},
   730  				Source: &Module{
   731  					FunctionSection: []Index{0},
   732  					TypeSection: []FunctionType{
   733  						{Params: []ValueType{}},
   734  					},
   735  				},
   736  			}
   737  			module := &Module{
   738  				TypeSection: []FunctionType{{Results: []ValueType{ValueTypeF32}}},
   739  				ImportPerModule: map[string][]*Import{
   740  					moduleName: {{Module: moduleName, Name: name, Type: ExternTypeFunc, DescFunc: 0}},
   741  				},
   742  			}
   743  
   744  			m := &ModuleInstance{Engine: &mockModuleEngine{resolveImportsCalled: map[Index]Index{}}, s: s, Source: module}
   745  			err := m.resolveImports(module)
   746  			require.EqualError(t, err, "import func[test.target]: signature mismatch: v_f32 != v_v")
   747  		})
   748  	})
   749  	t.Run("global", func(t *testing.T) {
   750  		t.Run("ok", func(t *testing.T) {
   751  			s := newStore()
   752  			g := &GlobalInstance{Type: GlobalType{ValType: ValueTypeI32}}
   753  			m := &ModuleInstance{Globals: make([]*GlobalInstance, 1), s: s}
   754  			s.nameToModule[moduleName] = &ModuleInstance{
   755  				Globals: []*GlobalInstance{g},
   756  				Exports: map[string]*Export{name: {Type: ExternTypeGlobal, Index: 0}}, ModuleName: moduleName,
   757  			}
   758  			err := m.resolveImports(
   759  				&Module{
   760  					ImportPerModule: map[string][]*Import{moduleName: {{Name: name, Type: ExternTypeGlobal, DescGlobal: g.Type}}},
   761  				},
   762  			)
   763  			require.NoError(t, err)
   764  			require.True(t, globalsContain(m.Globals, g), "expected to find %v in %v", g, m.Globals)
   765  		})
   766  		t.Run("mutability mismatch", func(t *testing.T) {
   767  			s := newStore()
   768  			s.nameToModule[moduleName] = &ModuleInstance{
   769  				Globals: []*GlobalInstance{{Type: GlobalType{Mutable: false}}},
   770  				Exports: map[string]*Export{name: {
   771  					Type:  ExternTypeGlobal,
   772  					Index: 0,
   773  				}},
   774  				ModuleName: moduleName,
   775  			}
   776  			m := &ModuleInstance{Globals: make([]*GlobalInstance, 1), s: s}
   777  			err := m.resolveImports(&Module{
   778  				ImportPerModule: map[string][]*Import{moduleName: {
   779  					{Module: moduleName, Name: name, Type: ExternTypeGlobal, DescGlobal: GlobalType{Mutable: true}},
   780  				}},
   781  			})
   782  			require.EqualError(t, err, "import global[test.target]: mutability mismatch: true != false")
   783  		})
   784  		t.Run("type mismatch", func(t *testing.T) {
   785  			s := newStore()
   786  			s.nameToModule[moduleName] = &ModuleInstance{
   787  				Globals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}}},
   788  				Exports: map[string]*Export{name: {
   789  					Type:  ExternTypeGlobal,
   790  					Index: 0,
   791  				}},
   792  				ModuleName: moduleName,
   793  			}
   794  			m := &ModuleInstance{Globals: make([]*GlobalInstance, 1), s: s}
   795  			err := m.resolveImports(&Module{
   796  				ImportPerModule: map[string][]*Import{moduleName: {
   797  					{Module: moduleName, Name: name, Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeF64}},
   798  				}},
   799  			})
   800  			require.EqualError(t, err, "import global[test.target]: value type mismatch: f64 != i32")
   801  		})
   802  	})
   803  	t.Run("memory", func(t *testing.T) {
   804  		t.Run("ok", func(t *testing.T) {
   805  			max := uint32(10)
   806  			memoryInst := &MemoryInstance{Max: max}
   807  			s := newStore()
   808  			s.nameToModule[moduleName] = &ModuleInstance{
   809  				MemoryInstance: memoryInst,
   810  				Exports: map[string]*Export{name: {
   811  					Type: ExternTypeMemory,
   812  				}},
   813  				ModuleName: moduleName,
   814  			}
   815  			m := &ModuleInstance{s: s}
   816  			err := m.resolveImports(&Module{
   817  				ImportPerModule: map[string][]*Import{
   818  					moduleName: {{Module: moduleName, Name: name, Type: ExternTypeMemory, DescMem: &Memory{Max: max}}},
   819  				},
   820  			})
   821  			require.NoError(t, err)
   822  			require.Equal(t, m.MemoryInstance, memoryInst)
   823  		})
   824  		t.Run("minimum size mismatch", func(t *testing.T) {
   825  			importMemoryType := &Memory{Min: 2, Cap: 2}
   826  			s := newStore()
   827  			s.nameToModule[moduleName] = &ModuleInstance{
   828  				MemoryInstance: &MemoryInstance{Min: importMemoryType.Min - 1, Cap: 2},
   829  				Exports: map[string]*Export{name: {
   830  					Type: ExternTypeMemory,
   831  				}},
   832  				ModuleName: moduleName,
   833  			}
   834  			m := &ModuleInstance{s: s}
   835  			err := m.resolveImports(&Module{
   836  				ImportPerModule: map[string][]*Import{
   837  					moduleName: {{Module: moduleName, Name: name, Type: ExternTypeMemory, DescMem: importMemoryType}},
   838  				},
   839  			})
   840  			require.EqualError(t, err, "import memory[test.target]: minimum size mismatch: 2 > 1")
   841  		})
   842  		t.Run("maximum size mismatch", func(t *testing.T) {
   843  			s := newStore()
   844  			s.nameToModule[moduleName] = &ModuleInstance{
   845  				MemoryInstance: &MemoryInstance{Max: MemoryLimitPages},
   846  				Exports: map[string]*Export{name: {
   847  					Type: ExternTypeMemory,
   848  				}},
   849  				ModuleName: moduleName,
   850  			}
   851  
   852  			max := uint32(10)
   853  			importMemoryType := &Memory{Max: max}
   854  			m := &ModuleInstance{s: s}
   855  			err := m.resolveImports(&Module{
   856  				ImportPerModule: map[string][]*Import{moduleName: {{Module: moduleName, Name: name, Type: ExternTypeMemory, DescMem: importMemoryType}}},
   857  			})
   858  			require.EqualError(t, err, "import memory[test.target]: maximum size mismatch: 10 < 65536")
   859  		})
   860  	})
   861  }
   862  
   863  func TestModuleInstance_validateData(t *testing.T) {
   864  	m := &ModuleInstance{MemoryInstance: &MemoryInstance{Buffer: make([]byte, 5)}}
   865  	tests := []struct {
   866  		name   string
   867  		data   []DataSegment
   868  		expErr string
   869  	}{
   870  		{
   871  			name: "ok",
   872  			data: []DataSegment{
   873  				{OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []byte{0}},
   874  				{OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)}, Init: []byte{0}},
   875  			},
   876  		},
   877  		{
   878  			name: "out of bounds - single one byte",
   879  			data: []DataSegment{
   880  				{OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(5)}, Init: []byte{0}},
   881  			},
   882  			expErr: "data[0]: out of bounds memory access",
   883  		},
   884  		{
   885  			name: "out of bounds - multi bytes",
   886  			data: []DataSegment{
   887  				{OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(0)}, Init: []byte{0}},
   888  				{OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(3)}, Init: []byte{0, 1, 2}},
   889  			},
   890  			expErr: "data[1]: out of bounds memory access",
   891  		},
   892  	}
   893  
   894  	for _, tt := range tests {
   895  		tc := tt
   896  		t.Run(tc.name, func(t *testing.T) {
   897  			err := m.validateData(tc.data)
   898  			if tc.expErr != "" {
   899  				require.EqualError(t, err, tc.expErr)
   900  			} else {
   901  				require.NoError(t, err)
   902  			}
   903  		})
   904  	}
   905  }
   906  
   907  func TestModuleInstance_applyData(t *testing.T) {
   908  	t.Run("ok", func(t *testing.T) {
   909  		m := &ModuleInstance{MemoryInstance: &MemoryInstance{Buffer: make([]byte, 10)}}
   910  		err := m.applyData([]DataSegment{
   911  			{OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: const0}, Init: []byte{0xa, 0xf}},
   912  			{OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeUint32(8)}, Init: []byte{0x1, 0x5}},
   913  		})
   914  		require.NoError(t, err)
   915  		require.Equal(t, []byte{0xa, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5}, m.MemoryInstance.Buffer)
   916  		require.Equal(t, [][]byte{{0xa, 0xf}, {0x1, 0x5}}, m.DataInstances)
   917  	})
   918  	t.Run("error", func(t *testing.T) {
   919  		m := &ModuleInstance{MemoryInstance: &MemoryInstance{Buffer: make([]byte, 5)}}
   920  		err := m.applyData([]DataSegment{
   921  			{OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeUint32(8)}, Init: []byte{}},
   922  		})
   923  		require.EqualError(t, err, "data[0]: out of bounds memory access")
   924  	})
   925  }
   926  
   927  func globalsContain(globals []*GlobalInstance, want *GlobalInstance) bool {
   928  	for _, f := range globals {
   929  		if f == want {
   930  			return true
   931  		}
   932  	}
   933  	return false
   934  }
   935  
   936  func TestModuleInstance_applyElements(t *testing.T) {
   937  	leb128_100 := leb128.EncodeInt32(100)
   938  
   939  	t.Run("extenref", func(t *testing.T) {
   940  		m := &ModuleInstance{}
   941  		m.Tables = []*TableInstance{{Type: RefTypeExternref, References: make([]Reference, 10)}}
   942  		for i := range m.Tables[0].References {
   943  			m.Tables[0].References[i] = 0xffff // non-null ref.
   944  		}
   945  
   946  		// This shouldn't panic.
   947  		m.applyElements([]ElementSegment{{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128_100}}})
   948  		m.applyElements([]ElementSegment{
   949  			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: make([]Index, 3)},
   950  			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128_100}, Init: make([]Index, 5)}, // Iteration stops at this point, so the offset:5 below shouldn't be applied.
   951  			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: make([]Index, 5)},
   952  		})
   953  		require.Equal(t, []Reference{0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
   954  			m.Tables[0].References)
   955  		m.applyElements([]ElementSegment{
   956  			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: make([]Index, 5)},
   957  		})
   958  		require.Equal(t, []Reference{0, 0, 0, 0xffff, 0xffff, 0, 0, 0, 0, 0}, m.Tables[0].References)
   959  	})
   960  	t.Run("funcref", func(t *testing.T) {
   961  		e := &mockEngine{}
   962  		me, err := e.NewModuleEngine(nil, nil)
   963  		me.(*mockModuleEngine).functionRefs = map[Index]Reference{0: 0xa, 1: 0xaa, 2: 0xaaa, 3: 0xaaaa}
   964  		require.NoError(t, err)
   965  		m := &ModuleInstance{Engine: me, Globals: []*GlobalInstance{{}, {Val: 0xabcde}}}
   966  
   967  		m.Tables = []*TableInstance{{Type: RefTypeFuncref, References: make([]Reference, 10)}}
   968  		for i := range m.Tables[0].References {
   969  			m.Tables[0].References[i] = 0xffff // non-null ref.
   970  		}
   971  
   972  		// This shouldn't panic.
   973  		m.applyElements([]ElementSegment{{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128_100}, Init: []Index{1, 2, 3}}})
   974  		m.applyElements([]ElementSegment{
   975  			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0, 1, 2}},
   976  			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{9}}, Init: []Index{1 | ElementInitImportedGlobalFunctionReference}},
   977  			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128_100}, Init: make([]Index, 5)}, // Iteration stops at this point, so the offset:5 below shouldn't be applied.
   978  			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: make([]Index, 5)},
   979  		})
   980  		require.Equal(t, []Reference{0xa, 0xaa, 0xaaa, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xabcde},
   981  			m.Tables[0].References)
   982  		m.applyElements([]ElementSegment{
   983  			{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: []Index{0, ElementInitNullReference, 2}},
   984  		})
   985  		require.Equal(t, []Reference{0xa, 0xaa, 0xaaa, 0xffff, 0xffff, 0xa, 0xffff, 0xaaa, 0xffff, 0xabcde},
   986  			m.Tables[0].References)
   987  	})
   988  }