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