github.com/tetratelabs/wazero@v1.2.1/internal/testing/enginetest/enginetest.go (about)

     1  // Package enginetest contains tests common to any wasm.Engine implementation. Defining these as top-level
     2  // functions is less burden than copy/pasting the implementations, while still allowing test caching to operate.
     3  //
     4  // In simplest case, dispatch:
     5  //
     6  //	func TestModuleEngine_Call(t *testing.T) {
     7  //		enginetest.RunTestModuleEngineCall(t, NewEngine)
     8  //	}
     9  //
    10  // Some tests using the Compiler Engine may need to guard as they use compiled features:
    11  //
    12  //	func TestModuleEngine_Call(t *testing.T) {
    13  //		requireSupportedOSArch(t)
    14  //		enginetest.RunTestModuleEngineCall(t, NewEngine)
    15  //	}
    16  //
    17  // Note: These tests intentionally avoid using wasm.Store as it is important to know both the dependencies and
    18  // the capabilities at the wasm.Engine abstraction.
    19  package enginetest
    20  
    21  import (
    22  	"context"
    23  	"debug/dwarf"
    24  	"errors"
    25  	"math"
    26  	"strings"
    27  	"testing"
    28  
    29  	"github.com/tetratelabs/wazero/api"
    30  	"github.com/tetratelabs/wazero/experimental"
    31  	"github.com/tetratelabs/wazero/internal/leb128"
    32  	"github.com/tetratelabs/wazero/internal/testing/require"
    33  	"github.com/tetratelabs/wazero/internal/u64"
    34  	"github.com/tetratelabs/wazero/internal/wasm"
    35  	"github.com/tetratelabs/wazero/internal/wasmdebug"
    36  	"github.com/tetratelabs/wazero/internal/wasmruntime"
    37  )
    38  
    39  const (
    40  	i32, i64 = wasm.ValueTypeI32, wasm.ValueTypeI64
    41  )
    42  
    43  // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
    44  var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
    45  
    46  type EngineTester interface {
    47  	NewEngine(enabledFeatures api.CoreFeatures) wasm.Engine
    48  
    49  	ListenerFactory() experimental.FunctionListenerFactory
    50  }
    51  
    52  // RunTestEngineMemoryGrowInRecursiveCall ensures that it's safe to grow memory in the recursive Wasm calls.
    53  func RunTestEngineMemoryGrowInRecursiveCall(t *testing.T, et EngineTester) {
    54  	enabledFeatures := api.CoreFeaturesV1
    55  	e := et.NewEngine(enabledFeatures)
    56  	s := wasm.NewStore(enabledFeatures, e)
    57  
    58  	const hostModuleName = "env"
    59  	const hostFnName = "grow_memory"
    60  	var growFn api.Function
    61  	hm, err := wasm.NewHostModule(
    62  		hostModuleName,
    63  		[]string{hostFnName},
    64  		map[string]*wasm.HostFunc{
    65  			hostFnName: {
    66  				ExportName: hostFnName,
    67  				Code: wasm.Code{GoFunc: func() {
    68  					// Does the recursive call into Wasm, which grows memory.
    69  					_, err := growFn.Call(context.Background())
    70  					require.NoError(t, err)
    71  				}},
    72  			},
    73  		},
    74  		enabledFeatures,
    75  	)
    76  	require.NoError(t, err)
    77  
    78  	err = s.Engine.CompileModule(testCtx, hm, nil, false)
    79  	require.NoError(t, err)
    80  
    81  	typeIDs, err := s.GetFunctionTypeIDs(hm.TypeSection)
    82  	require.NoError(t, err)
    83  
    84  	_, err = s.Instantiate(testCtx, hm, hostModuleName, nil, typeIDs)
    85  	require.NoError(t, err)
    86  
    87  	m := &wasm.Module{
    88  		ImportFunctionCount: 1,
    89  		TypeSection:         []wasm.FunctionType{{Params: []wasm.ValueType{}, Results: []wasm.ValueType{}}},
    90  		FunctionSection:     []wasm.Index{0, 0},
    91  		CodeSection: []wasm.Code{
    92  			{
    93  				Body: []byte{
    94  					// Calls the imported host function, which in turn calls the next in-Wasm function recursively.
    95  					wasm.OpcodeCall, 0,
    96  					// Access the memory and this should succeed as we already had memory grown at this point.
    97  					wasm.OpcodeI32Const, 0,
    98  					wasm.OpcodeI32Load, 0x2, 0x0,
    99  					wasm.OpcodeDrop,
   100  					wasm.OpcodeEnd,
   101  				},
   102  			},
   103  			{
   104  				// Grows memory by 1 page.
   105  				Body: []byte{wasm.OpcodeI32Const, 1, wasm.OpcodeMemoryGrow, wasm.OpcodeDrop, wasm.OpcodeEnd},
   106  			},
   107  		},
   108  		MemorySection:   &wasm.Memory{Max: 1000},
   109  		ImportSection:   []wasm.Import{{Module: hostModuleName, Name: hostFnName, DescFunc: 0}},
   110  		ImportPerModule: map[string][]*wasm.Import{hostModuleName: {{Module: hostModuleName, Name: hostFnName, DescFunc: 0}}},
   111  	}
   112  	m.BuildMemoryDefinitions()
   113  
   114  	err = s.Engine.CompileModule(testCtx, m, nil, false)
   115  	require.NoError(t, err)
   116  
   117  	typeIDs, err = s.GetFunctionTypeIDs(m.TypeSection)
   118  	require.NoError(t, err)
   119  
   120  	inst, err := s.Instantiate(testCtx, m, t.Name(), nil, typeIDs)
   121  	require.NoError(t, err)
   122  
   123  	growFn = inst.Engine.NewFunction(2)
   124  	_, err = inst.Engine.NewFunction(1).Call(context.Background())
   125  	require.NoError(t, err)
   126  }
   127  
   128  func RunTestEngineNewModuleEngine(t *testing.T, et EngineTester) {
   129  	e := et.NewEngine(api.CoreFeaturesV1)
   130  
   131  	t.Run("error before instantiation", func(t *testing.T) {
   132  		_, err := e.NewModuleEngine(&wasm.Module{}, nil)
   133  		require.EqualError(t, err, "source module must be compiled before instantiation")
   134  	})
   135  }
   136  
   137  func RunTestModuleEngineCall(t *testing.T, et EngineTester) {
   138  	e := et.NewEngine(api.CoreFeaturesV2)
   139  
   140  	// Define a basic function which defines two parameters and two results.
   141  	// This is used to test results when incorrect arity is used.
   142  	m := &wasm.Module{
   143  		TypeSection: []wasm.FunctionType{
   144  			{
   145  				Params:            []wasm.ValueType{i64, i64},
   146  				Results:           []wasm.ValueType{i64, i64},
   147  				ParamNumInUint64:  2,
   148  				ResultNumInUint64: 2,
   149  			},
   150  		},
   151  		FunctionSection: []wasm.Index{0},
   152  		CodeSection: []wasm.Code{
   153  			{Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 1, wasm.OpcodeEnd}},
   154  		},
   155  	}
   156  
   157  	listeners := buildFunctionListeners(et.ListenerFactory(), m)
   158  	err := e.CompileModule(testCtx, m, listeners, false)
   159  	require.NoError(t, err)
   160  
   161  	// To use the function, we first need to add it to a module.
   162  	module := &wasm.ModuleInstance{ModuleName: t.Name(), TypeIDs: []wasm.FunctionTypeID{0}}
   163  
   164  	// Compile the module
   165  	me, err := e.NewModuleEngine(m, module)
   166  	require.NoError(t, err)
   167  	linkModuleToEngine(module, me)
   168  
   169  	// Ensure the base case doesn't fail: A single parameter should work as that matches the function signature.
   170  	const funcIndex = 0
   171  	ce := me.NewFunction(funcIndex)
   172  
   173  	results, err := ce.Call(testCtx, 1, 2)
   174  	require.NoError(t, err)
   175  	require.Equal(t, []uint64{1, 2}, results)
   176  
   177  	t.Run("errs when not enough parameters", func(t *testing.T) {
   178  		ce := me.NewFunction(funcIndex)
   179  		_, err = ce.Call(testCtx)
   180  		require.EqualError(t, err, "expected 2 params, but passed 0")
   181  	})
   182  
   183  	t.Run("errs when too many parameters", func(t *testing.T) {
   184  		ce := me.NewFunction(funcIndex)
   185  		_, err = ce.Call(testCtx, 1, 2, 3)
   186  		require.EqualError(t, err, "expected 2 params, but passed 3")
   187  	})
   188  }
   189  
   190  func RunTestModuleEngineCallWithStack(t *testing.T, et EngineTester) {
   191  	e := et.NewEngine(api.CoreFeaturesV2)
   192  
   193  	// Define a basic function which defines two parameters and two results.
   194  	// This is used to test results when incorrect arity is used.
   195  	m := &wasm.Module{
   196  		TypeSection: []wasm.FunctionType{
   197  			{
   198  				Params:            []wasm.ValueType{i64, i64},
   199  				Results:           []wasm.ValueType{i64, i64},
   200  				ParamNumInUint64:  2,
   201  				ResultNumInUint64: 2,
   202  			},
   203  		},
   204  		FunctionSection: []wasm.Index{0},
   205  		CodeSection: []wasm.Code{
   206  			{Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 1, wasm.OpcodeEnd}},
   207  		},
   208  	}
   209  
   210  	listeners := buildFunctionListeners(et.ListenerFactory(), m)
   211  	err := e.CompileModule(testCtx, m, listeners, false)
   212  	require.NoError(t, err)
   213  
   214  	// To use the function, we first need to add it to a module.
   215  	module := &wasm.ModuleInstance{ModuleName: t.Name(), TypeIDs: []wasm.FunctionTypeID{0}}
   216  
   217  	// Compile the module
   218  	me, err := e.NewModuleEngine(m, module)
   219  	require.NoError(t, err)
   220  	linkModuleToEngine(module, me)
   221  
   222  	// Ensure the base case doesn't fail: A single parameter should work as that matches the function signature.
   223  	const funcIndex = 0
   224  	ce := me.NewFunction(funcIndex)
   225  
   226  	stack := []uint64{1, 2}
   227  	err = ce.CallWithStack(testCtx, stack)
   228  	require.NoError(t, err)
   229  	require.Equal(t, []uint64{1, 2}, stack)
   230  
   231  	t.Run("errs when not enough parameters", func(t *testing.T) {
   232  		ce := me.NewFunction(funcIndex)
   233  		err = ce.CallWithStack(testCtx, nil)
   234  		require.EqualError(t, err, "need 2 params, but stack size is 0")
   235  	})
   236  }
   237  
   238  func RunTestModuleEngineLookupFunction(t *testing.T, et EngineTester) {
   239  	e := et.NewEngine(api.CoreFeaturesV1)
   240  
   241  	mod := &wasm.Module{
   242  		TypeSection:     []wasm.FunctionType{{}, {Params: []wasm.ValueType{wasm.ValueTypeV128}}},
   243  		FunctionSection: []wasm.Index{0, 0, 0},
   244  		CodeSection: []wasm.Code{
   245  			{
   246  				Body: []byte{wasm.OpcodeEnd},
   247  			}, {Body: []byte{wasm.OpcodeEnd}}, {Body: []byte{wasm.OpcodeEnd}},
   248  		},
   249  	}
   250  
   251  	err := e.CompileModule(testCtx, mod, nil, false)
   252  	require.NoError(t, err)
   253  	m := &wasm.ModuleInstance{
   254  		TypeIDs: []wasm.FunctionTypeID{0, 1},
   255  	}
   256  	m.Tables = []*wasm.TableInstance{
   257  		{Min: 2, References: make([]wasm.Reference, 2), Type: wasm.RefTypeFuncref},
   258  		{Min: 2, References: make([]wasm.Reference, 2), Type: wasm.RefTypeExternref},
   259  		{Min: 10, References: make([]wasm.Reference, 10), Type: wasm.RefTypeFuncref},
   260  	}
   261  
   262  	me, err := e.NewModuleEngine(mod, m)
   263  	require.NoError(t, err)
   264  	linkModuleToEngine(m, me)
   265  
   266  	t.Run("null reference", func(t *testing.T) {
   267  		_, err := me.LookupFunction(m.Tables[0], m.TypeIDs[0], 0) // offset 0 is not initialized yet.
   268  		require.Equal(t, wasmruntime.ErrRuntimeInvalidTableAccess, err)
   269  		_, err = me.LookupFunction(m.Tables[0], m.TypeIDs[0], 1) // offset 1 is not initialized yet.
   270  		require.Equal(t, wasmruntime.ErrRuntimeInvalidTableAccess, err)
   271  	})
   272  
   273  	m.Tables[0].References[0] = me.FunctionInstanceReference(2)
   274  	m.Tables[0].References[1] = me.FunctionInstanceReference(0)
   275  
   276  	t.Run("initialized", func(t *testing.T) {
   277  		f1, err := me.LookupFunction(m.Tables[0], m.TypeIDs[0], 0) // offset 0 is now initialized.
   278  		require.NoError(t, err)
   279  		require.Equal(t, wasm.Index(2), f1.Definition().Index())
   280  		f2, err := me.LookupFunction(m.Tables[0], m.TypeIDs[0], 1) // offset 1 is now initialized.
   281  		require.NoError(t, err)
   282  		require.Equal(t, wasm.Index(0), f2.Definition().Index())
   283  	})
   284  
   285  	t.Run("out of range", func(t *testing.T) {
   286  		_, err := me.LookupFunction(m.Tables[0], m.TypeIDs[0], 100 /* out of range */)
   287  		require.Equal(t, wasmruntime.ErrRuntimeInvalidTableAccess, err)
   288  	})
   289  
   290  	t.Run("access to externref table", func(t *testing.T) {
   291  		_, err := me.LookupFunction(m.Tables[1], /* table[1] has externref type. */
   292  			m.TypeIDs[0], 0)
   293  		require.Equal(t, wasmruntime.ErrRuntimeInvalidTableAccess, err)
   294  	})
   295  
   296  	t.Run("access to externref table", func(t *testing.T) {
   297  		_, err := me.LookupFunction(m.Tables[0], /* type mismatch */
   298  			m.TypeIDs[1], 0)
   299  		require.Equal(t, wasmruntime.ErrRuntimeIndirectCallTypeMismatch, err)
   300  	})
   301  
   302  	m.Tables[2].References[0] = me.FunctionInstanceReference(1)
   303  	m.Tables[2].References[5] = me.FunctionInstanceReference(2)
   304  	t.Run("initialized - tables[2]", func(t *testing.T) {
   305  		f1, err := me.LookupFunction(m.Tables[2], m.TypeIDs[0], 0)
   306  		require.NoError(t, err)
   307  		require.Equal(t, wasm.Index(1), f1.Definition().Index())
   308  		f2, err := me.LookupFunction(m.Tables[2], m.TypeIDs[0], 5)
   309  		require.NoError(t, err)
   310  		require.Equal(t, wasm.Index(2), f2.Definition().Index())
   311  	})
   312  }
   313  
   314  func runTestModuleEngineCallHostFnMem(t *testing.T, et EngineTester, readMem *wasm.Code) {
   315  	e := et.NewEngine(api.CoreFeaturesV1)
   316  	defer e.Close()
   317  	importing := setupCallMemTests(t, e, readMem)
   318  
   319  	importingMemoryVal := uint64(6)
   320  	importing.MemoryInstance = &wasm.MemoryInstance{Buffer: u64.LeBytes(importingMemoryVal), Min: 1, Cap: 1, Max: 1}
   321  
   322  	tests := []struct {
   323  		name     string
   324  		fn       wasm.Index
   325  		expected uint64
   326  	}{
   327  		{
   328  			name:     callImportReadMemName,
   329  			fn:       importing.Exports[callImportReadMemName].Index,
   330  			expected: importingMemoryVal,
   331  		},
   332  	}
   333  	for _, tt := range tests {
   334  		tc := tt
   335  
   336  		t.Run(tc.name, func(t *testing.T) {
   337  			ce := importing.Engine.NewFunction(tc.fn)
   338  
   339  			results, err := ce.Call(testCtx)
   340  			require.NoError(t, err)
   341  			require.Equal(t, tc.expected, results[0])
   342  		})
   343  	}
   344  }
   345  
   346  func RunTestModuleEngineCallHostFn(t *testing.T, et EngineTester) {
   347  	t.Run("wasm", func(t *testing.T) {
   348  		runTestModuleEngineCallHostFn(t, et, hostDivByWasm)
   349  	})
   350  	t.Run("go", func(t *testing.T) {
   351  		runTestModuleEngineCallHostFn(t, et, &hostDivByGo)
   352  		runTestModuleEngineCallHostFnMem(t, et, &hostReadMemGo)
   353  	})
   354  }
   355  
   356  func runTestModuleEngineCallHostFn(t *testing.T, et EngineTester, hostDivBy *wasm.Code) {
   357  	e := et.NewEngine(api.CoreFeaturesV1)
   358  	defer e.Close()
   359  
   360  	imported, importing := setupCallTests(t, e, hostDivBy, et.ListenerFactory())
   361  
   362  	// Ensure the base case doesn't fail: A single parameter should work as that matches the function signature.
   363  	tests := []struct {
   364  		name   string
   365  		module *wasm.ModuleInstance
   366  		fn     wasm.Index
   367  	}{
   368  		{
   369  			name:   divByWasmName,
   370  			module: imported,
   371  			fn:     imported.Exports[divByWasmName].Index,
   372  		},
   373  		{
   374  			name:   callDivByGoName,
   375  			module: imported,
   376  			fn:     imported.Exports[callDivByGoName].Index,
   377  		},
   378  		{
   379  			name:   callImportCallDivByGoName,
   380  			module: importing,
   381  			fn:     importing.Exports[callImportCallDivByGoName].Index,
   382  		},
   383  	}
   384  	for _, tt := range tests {
   385  		tc := tt
   386  
   387  		t.Run(tc.name, func(t *testing.T) {
   388  			f := tc.fn
   389  
   390  			ce := tc.module.Engine.NewFunction(f)
   391  
   392  			results, err := ce.Call(testCtx, 1)
   393  			require.NoError(t, err)
   394  			require.Equal(t, uint64(1), results[0])
   395  
   396  			results2, err := ce.Call(testCtx, 1)
   397  			require.NoError(t, err)
   398  			require.Equal(t, results, results2)
   399  
   400  			// Ensure the result slices are unique
   401  			results[0] = 255
   402  			require.Equal(t, uint64(1), results2[0])
   403  		})
   404  	}
   405  }
   406  
   407  func RunTestModuleEngine_Call_Errors(t *testing.T, et EngineTester) {
   408  	e := et.NewEngine(api.CoreFeaturesV1)
   409  	defer e.Close()
   410  
   411  	imported, importing := setupCallTests(t, e, &hostDivByGo, et.ListenerFactory())
   412  
   413  	tests := []struct {
   414  		name        string
   415  		module      *wasm.ModuleInstance
   416  		fn          wasm.Index
   417  		input       []uint64
   418  		expectedErr string
   419  	}{
   420  		{
   421  			name:        "wasm function not enough parameters",
   422  			input:       []uint64{},
   423  			module:      imported,
   424  			fn:          imported.Exports[divByWasmName].Index,
   425  			expectedErr: `expected 1 params, but passed 0`,
   426  		},
   427  		{
   428  			name:        "wasm function too many parameters",
   429  			input:       []uint64{1, 2},
   430  			module:      imported,
   431  			fn:          imported.Exports[divByWasmName].Index,
   432  			expectedErr: `expected 1 params, but passed 2`,
   433  		},
   434  		{
   435  			name:   "wasm function panics with wasmruntime.Error",
   436  			input:  []uint64{0},
   437  			module: imported,
   438  			fn:     imported.Exports[divByWasmName].Index,
   439  			expectedErr: `wasm error: integer divide by zero
   440  wasm stack trace:
   441  	imported.div_by.wasm(i32) i32`,
   442  		},
   443  		{
   444  			name:   "wasm calls host function that panics",
   445  			input:  []uint64{math.MaxUint32},
   446  			module: imported,
   447  			fn:     imported.Exports[callDivByGoName].Index,
   448  			expectedErr: `host-function panic (recovered by wazero)
   449  wasm stack trace:
   450  	host.div_by.go(i32) i32
   451  	imported.call->div_by.go(i32) i32`,
   452  		},
   453  		{
   454  			name:   "wasm calls imported wasm that calls host function panics with runtime.Error",
   455  			input:  []uint64{0},
   456  			module: importing,
   457  			fn:     importing.Exports[callImportCallDivByGoName].Index,
   458  			expectedErr: `runtime error: integer divide by zero (recovered by wazero)
   459  wasm stack trace:
   460  	host.div_by.go(i32) i32
   461  	imported.call->div_by.go(i32) i32
   462  	importing.call_import->call->div_by.go(i32) i32`,
   463  		},
   464  		{
   465  			name:   "wasm calls imported wasm that calls host function that panics",
   466  			input:  []uint64{math.MaxUint32},
   467  			module: importing,
   468  			fn:     importing.Exports[callImportCallDivByGoName].Index,
   469  			expectedErr: `host-function panic (recovered by wazero)
   470  wasm stack trace:
   471  	host.div_by.go(i32) i32
   472  	imported.call->div_by.go(i32) i32
   473  	importing.call_import->call->div_by.go(i32) i32`,
   474  		},
   475  		{
   476  			name:   "wasm calls imported wasm calls host function panics with runtime.Error",
   477  			input:  []uint64{0},
   478  			module: importing,
   479  			fn:     importing.Exports[callImportCallDivByGoName].Index,
   480  			expectedErr: `runtime error: integer divide by zero (recovered by wazero)
   481  wasm stack trace:
   482  	host.div_by.go(i32) i32
   483  	imported.call->div_by.go(i32) i32
   484  	importing.call_import->call->div_by.go(i32) i32`,
   485  		},
   486  	}
   487  	for _, tt := range tests {
   488  		tc := tt
   489  		t.Run(tc.name, func(t *testing.T) {
   490  			ce := tc.module.Engine.NewFunction(tc.fn)
   491  
   492  			_, err := ce.Call(testCtx, tc.input...)
   493  			require.NotNil(t, err)
   494  
   495  			errStr := err.Error()
   496  			// If this faces a Go runtime error, the error includes the Go stack trace which makes the test unstable,
   497  			// so we trim them here.
   498  			if index := strings.Index(errStr, wasmdebug.GoRuntimeErrorTracePrefix); index > -1 {
   499  				errStr = strings.TrimSpace(errStr[:index])
   500  			}
   501  			require.Equal(t, errStr, tc.expectedErr)
   502  
   503  			// Ensure the module still works
   504  			results, err := ce.Call(testCtx, 1)
   505  			require.NoError(t, err)
   506  			require.Equal(t, uint64(1), results[0])
   507  		})
   508  	}
   509  }
   510  
   511  // RunTestModuleEngineBeforeListenerStackIterator tests that the StackIterator provided by the Engine to the Before hook
   512  // of the listener is properly able to walk the stack.  As an example, it
   513  // validates that the following call stack is properly walked:
   514  //
   515  //  1. f1(2,3,4) [no return, no local]
   516  //  2. calls f2(no arg) [1 return, 1 local]
   517  //  3. calls f3(5) [1 return, no local]
   518  //  4. calls f4(6) [1 return, HOST]
   519  func RunTestModuleEngineBeforeListenerStackIterator(t *testing.T, et EngineTester) {
   520  	e := et.NewEngine(api.CoreFeaturesV2)
   521  
   522  	type stackEntry struct {
   523  		debugName string
   524  		args      []uint64
   525  	}
   526  
   527  	expectedCallstacks := [][]stackEntry{
   528  		{ // when calling f1
   529  			{debugName: "whatever.f1", args: []uint64{2, 3, 4}},
   530  		},
   531  		{ // when calling f2
   532  			{debugName: "whatever.f2", args: []uint64{}},
   533  			{debugName: "whatever.f1", args: []uint64{2, 3, 4}},
   534  		},
   535  		{ // when calling f3
   536  			{debugName: "whatever.f3", args: []uint64{5}},
   537  			{debugName: "whatever.f2", args: []uint64{}},
   538  			{debugName: "whatever.f1", args: []uint64{2, 3, 4}},
   539  		},
   540  		{ // when calling f4
   541  			{debugName: "whatever.f4", args: []uint64{6}},
   542  			{debugName: "whatever.f3", args: []uint64{5}},
   543  			{debugName: "whatever.f2", args: []uint64{}},
   544  			{debugName: "whatever.f1", args: []uint64{2, 3, 4}},
   545  		},
   546  	}
   547  
   548  	fnListener := &fnListener{
   549  		beforeFn: func(ctx context.Context, mod api.Module, def api.FunctionDefinition, params []uint64, si experimental.StackIterator) {
   550  			require.True(t, len(expectedCallstacks) > 0)
   551  			expectedCallstack := expectedCallstacks[0]
   552  			for si.Next() {
   553  				require.True(t, len(expectedCallstack) > 0)
   554  				require.Equal(t, expectedCallstack[0].debugName, si.Function().Definition().DebugName())
   555  				require.Equal(t, expectedCallstack[0].args, si.Parameters())
   556  				expectedCallstack = expectedCallstack[1:]
   557  			}
   558  			require.Equal(t, 0, len(expectedCallstack))
   559  			expectedCallstacks = expectedCallstacks[1:]
   560  		},
   561  	}
   562  
   563  	functionTypes := []wasm.FunctionType{
   564  		// f1 type
   565  		{
   566  			Params:            []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32},
   567  			ParamNumInUint64:  3,
   568  			Results:           []api.ValueType{},
   569  			ResultNumInUint64: 0,
   570  		},
   571  		// f2 type
   572  		{
   573  			Params:            []api.ValueType{},
   574  			ParamNumInUint64:  0,
   575  			Results:           []api.ValueType{api.ValueTypeI32},
   576  			ResultNumInUint64: 1,
   577  		},
   578  		// f3 type
   579  		{
   580  			Params:            []api.ValueType{api.ValueTypeI32},
   581  			ParamNumInUint64:  1,
   582  			Results:           []api.ValueType{api.ValueTypeI32},
   583  			ResultNumInUint64: 1,
   584  		},
   585  		// f4 type
   586  		{
   587  			Params:            []api.ValueType{api.ValueTypeI32},
   588  			ParamNumInUint64:  1,
   589  			Results:           []api.ValueType{api.ValueTypeI32},
   590  			ResultNumInUint64: 1,
   591  		},
   592  	}
   593  
   594  	hostgofn := wasm.MustParseGoReflectFuncCode(func(x int32) int32 {
   595  		return x + 100
   596  	})
   597  
   598  	m := &wasm.Module{
   599  		TypeSection:     functionTypes,
   600  		FunctionSection: []wasm.Index{0, 1, 2, 3},
   601  		NameSection: &wasm.NameSection{
   602  			ModuleName: "whatever",
   603  			FunctionNames: wasm.NameMap{
   604  				{Index: wasm.Index(0), Name: "f1"},
   605  				{Index: wasm.Index(1), Name: "f2"},
   606  				{Index: wasm.Index(2), Name: "f3"},
   607  				{Index: wasm.Index(3), Name: "f4"},
   608  			},
   609  		},
   610  		CodeSection: []wasm.Code{
   611  			{ // f1
   612  				Body: []byte{
   613  					wasm.OpcodeI32Const, 0, // reserve return for f2
   614  					wasm.OpcodeCall,
   615  					1, // call f2
   616  					wasm.OpcodeEnd,
   617  				},
   618  			},
   619  			{ // f2
   620  				LocalTypes: []wasm.ValueType{wasm.ValueTypeI32},
   621  				Body: []byte{
   622  					wasm.OpcodeI32Const, 42, // local for f2
   623  					wasm.OpcodeLocalSet, 0,
   624  					wasm.OpcodeI32Const, 5, // argument of f3
   625  					wasm.OpcodeCall,
   626  					2, // call f3
   627  					wasm.OpcodeEnd,
   628  				},
   629  			},
   630  			{ // f3
   631  				Body: []byte{
   632  					wasm.OpcodeI32Const, 6,
   633  					wasm.OpcodeCall,
   634  					3, // call host function
   635  					wasm.OpcodeEnd,
   636  				},
   637  			},
   638  			// f4 [host function]
   639  			hostgofn,
   640  		},
   641  		ExportSection: []wasm.Export{
   642  			{Name: "f1", Type: wasm.ExternTypeFunc, Index: 0},
   643  		},
   644  		ID: wasm.ModuleID{0},
   645  	}
   646  
   647  	listeners := buildFunctionListeners(fnListener, m)
   648  	err := e.CompileModule(testCtx, m, listeners, false)
   649  	require.NoError(t, err)
   650  
   651  	module := &wasm.ModuleInstance{
   652  		ModuleName: t.Name(),
   653  		TypeIDs:    []wasm.FunctionTypeID{0, 1, 2, 3},
   654  		Exports:    exportMap(m),
   655  	}
   656  
   657  	me, err := e.NewModuleEngine(m, module)
   658  	require.NoError(t, err)
   659  	linkModuleToEngine(module, me)
   660  
   661  	initCallEngine := me.NewFunction(0) // f1
   662  	_, err = initCallEngine.Call(testCtx, 2, 3, 4)
   663  	require.NoError(t, err)
   664  	require.Equal(t, 0, len(expectedCallstacks))
   665  }
   666  
   667  // This tests that the Globals provided by the Engine to the Before hook of the
   668  // listener is properly able to read the values of the globals.
   669  func RunTestModuleEngineBeforeListenerGlobals(t *testing.T, et EngineTester) {
   670  	e := et.NewEngine(api.CoreFeaturesV2)
   671  
   672  	type globals struct {
   673  		values []uint64
   674  		types  []api.ValueType
   675  	}
   676  
   677  	expectedGlobals := []globals{
   678  		{values: []uint64{100, 200}, types: []api.ValueType{api.ValueTypeI32, api.ValueTypeI32}},
   679  		{values: []uint64{42, 11}, types: []api.ValueType{api.ValueTypeI32, api.ValueTypeI32}},
   680  	}
   681  
   682  	fnListener := &fnListener{
   683  		beforeFn: func(ctx context.Context, mod api.Module, def api.FunctionDefinition, params []uint64, si experimental.StackIterator) {
   684  			require.True(t, len(expectedGlobals) > 0)
   685  
   686  			imod := mod.(experimental.InternalModule)
   687  			expected := expectedGlobals[0]
   688  
   689  			require.Equal(t, len(expected.values), imod.NumGlobal())
   690  			for i := 0; i < imod.NumGlobal(); i++ {
   691  				global := imod.Global(i)
   692  				require.Equal(t, expected.types[i], global.Type())
   693  				require.Equal(t, expected.values[i], global.Get())
   694  			}
   695  
   696  			expectedGlobals = expectedGlobals[1:]
   697  		},
   698  	}
   699  
   700  	functionTypes := []wasm.FunctionType{
   701  		// f1 type
   702  		{
   703  			Params:            []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32},
   704  			ParamNumInUint64:  3,
   705  			Results:           []api.ValueType{},
   706  			ResultNumInUint64: 0,
   707  		},
   708  		// f2 type
   709  		{
   710  			Params:            []api.ValueType{},
   711  			ParamNumInUint64:  0,
   712  			Results:           []api.ValueType{api.ValueTypeI32},
   713  			ResultNumInUint64: 1,
   714  		},
   715  	}
   716  
   717  	m := &wasm.Module{
   718  		TypeSection:     functionTypes,
   719  		FunctionSection: []wasm.Index{0, 1},
   720  		NameSection: &wasm.NameSection{
   721  			ModuleName: "whatever",
   722  			FunctionNames: wasm.NameMap{
   723  				{Index: wasm.Index(0), Name: "f1"},
   724  				{Index: wasm.Index(1), Name: "f2"},
   725  			},
   726  		},
   727  		GlobalSection: []wasm.Global{
   728  			{
   729  				Type: wasm.GlobalType{ValType: wasm.ValueTypeI32, Mutable: true},
   730  				Init: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(100)},
   731  			},
   732  			{
   733  				Type: wasm.GlobalType{ValType: wasm.ValueTypeI32, Mutable: true},
   734  				Init: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(200)},
   735  			},
   736  		},
   737  		CodeSection: []wasm.Code{
   738  			{ // f1
   739  				Body: []byte{
   740  					wasm.OpcodeI32Const, 42,
   741  					wasm.OpcodeGlobalSet, 0, // store 42 in global 0
   742  					wasm.OpcodeI32Const, 11,
   743  					wasm.OpcodeGlobalSet, 1, // store 11 in global 1
   744  					wasm.OpcodeI32Const, 0, // reserve return for f2
   745  					wasm.OpcodeCall,
   746  					1, // call f2
   747  					wasm.OpcodeEnd,
   748  				},
   749  			},
   750  			{ // f2
   751  				LocalTypes: []wasm.ValueType{wasm.ValueTypeI32},
   752  				Body: []byte{
   753  					wasm.OpcodeI32Const, 42, // local for f2
   754  					wasm.OpcodeLocalSet, 0,
   755  					wasm.OpcodeEnd,
   756  				},
   757  			},
   758  		},
   759  		ExportSection: []wasm.Export{
   760  			{Name: "f1", Type: wasm.ExternTypeFunc, Index: 0},
   761  		},
   762  		ID: wasm.ModuleID{0},
   763  	}
   764  
   765  	listeners := buildFunctionListeners(fnListener, m)
   766  	err := e.CompileModule(testCtx, m, listeners, false)
   767  	require.NoError(t, err)
   768  
   769  	module := &wasm.ModuleInstance{
   770  		ModuleName: t.Name(),
   771  		TypeIDs:    []wasm.FunctionTypeID{0, 1, 2, 3},
   772  		Exports:    exportMap(m),
   773  		Globals: []*wasm.GlobalInstance{
   774  			{Val: 100, Type: wasm.GlobalType{ValType: wasm.ValueTypeI32, Mutable: true}},
   775  			{Val: 200, Type: wasm.GlobalType{ValType: wasm.ValueTypeI32, Mutable: true}},
   776  		},
   777  	}
   778  
   779  	me, err := e.NewModuleEngine(m, module)
   780  	require.NoError(t, err)
   781  	linkModuleToEngine(module, me)
   782  
   783  	initCallEngine := me.NewFunction(0) // f1
   784  	_, err = initCallEngine.Call(testCtx, 2, 3, 4)
   785  	require.NoError(t, err)
   786  	require.True(t, len(expectedGlobals) == 0)
   787  }
   788  
   789  type fnListener struct {
   790  	beforeFn func(context.Context, api.Module, api.FunctionDefinition, []uint64, experimental.StackIterator)
   791  	afterFn  func(context.Context, api.Module, api.FunctionDefinition, []uint64)
   792  	abortFn  func(context.Context, api.Module, api.FunctionDefinition, any)
   793  }
   794  
   795  func (f *fnListener) NewFunctionListener(api.FunctionDefinition) experimental.FunctionListener {
   796  	return f
   797  }
   798  
   799  func (f *fnListener) Before(ctx context.Context, mod api.Module, def api.FunctionDefinition, params []uint64, stackIterator experimental.StackIterator) {
   800  	if f.beforeFn != nil {
   801  		f.beforeFn(ctx, mod, def, params, stackIterator)
   802  	}
   803  }
   804  
   805  func (f *fnListener) After(ctx context.Context, mod api.Module, def api.FunctionDefinition, results []uint64) {
   806  	if f.afterFn != nil {
   807  		f.afterFn(ctx, mod, def, results)
   808  	}
   809  }
   810  
   811  func (f *fnListener) Abort(ctx context.Context, mod api.Module, def api.FunctionDefinition, err error) {
   812  	if f.abortFn != nil {
   813  		f.abortFn(ctx, mod, def, err)
   814  	}
   815  }
   816  
   817  func RunTestModuleEngineStackIteratorOffset(t *testing.T, et EngineTester) {
   818  	e := et.NewEngine(api.CoreFeaturesV2)
   819  
   820  	type frame struct {
   821  		function api.FunctionDefinition
   822  		offset   uint64
   823  	}
   824  
   825  	var tape [][]frame
   826  
   827  	fnListener := &fnListener{
   828  		beforeFn: func(ctx context.Context, mod api.Module, def api.FunctionDefinition, params []uint64, si experimental.StackIterator) {
   829  			var stack []frame
   830  			for si.Next() {
   831  				fn := si.Function()
   832  				pc := si.ProgramCounter()
   833  				stack = append(stack, frame{fn.Definition(), fn.SourceOffsetForPC(pc)})
   834  			}
   835  			tape = append(tape, stack)
   836  		},
   837  	}
   838  
   839  	functionTypes := []wasm.FunctionType{
   840  		// f1 type
   841  		{
   842  			Params:            []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32},
   843  			ParamNumInUint64:  3,
   844  			Results:           []api.ValueType{},
   845  			ResultNumInUint64: 0,
   846  		},
   847  		// f2 type
   848  		{
   849  			Params:            []api.ValueType{},
   850  			ParamNumInUint64:  0,
   851  			Results:           []api.ValueType{api.ValueTypeI32},
   852  			ResultNumInUint64: 1,
   853  		},
   854  		// f3 type
   855  		{
   856  			Params:            []api.ValueType{api.ValueTypeI32},
   857  			ParamNumInUint64:  1,
   858  			Results:           []api.ValueType{api.ValueTypeI32},
   859  			ResultNumInUint64: 1,
   860  		},
   861  	}
   862  
   863  	// Minimal DWARF info section to make debug/dwarf.New() happy.
   864  	// Necessary to make the compiler emit source offset maps.
   865  	info := []byte{
   866  		0x7, 0x0, 0x0, 0x0, // length (len(info) - 4)
   867  		0x3, 0x0, // version (between 3 and 5 makes it easier)
   868  		0x0, 0x0, 0x0, 0x0, // abbrev offset
   869  		0x0, // asize
   870  	}
   871  
   872  	d, err := dwarf.New(nil, nil, nil, info, nil, nil, nil, nil)
   873  	if err != nil {
   874  		panic(err)
   875  	}
   876  
   877  	hostgofn := wasm.MustParseGoReflectFuncCode(func(x int32) int32 {
   878  		return x + 100
   879  	})
   880  
   881  	m := &wasm.Module{
   882  		DWARFLines:      wasmdebug.NewDWARFLines(d),
   883  		TypeSection:     functionTypes,
   884  		FunctionSection: []wasm.Index{0, 1, 2},
   885  		NameSection: &wasm.NameSection{
   886  			ModuleName: "whatever",
   887  			FunctionNames: wasm.NameMap{
   888  				{Index: wasm.Index(0), Name: "f1"},
   889  				{Index: wasm.Index(1), Name: "f2"},
   890  				{Index: wasm.Index(2), Name: "f3"},
   891  			},
   892  		},
   893  		GlobalSection: []wasm.Global{
   894  			{
   895  				Type: wasm.GlobalType{ValType: wasm.ValueTypeI32, Mutable: true},
   896  				Init: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(100)},
   897  			},
   898  			{
   899  				Type: wasm.GlobalType{ValType: wasm.ValueTypeI32, Mutable: true},
   900  				Init: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(200)},
   901  			},
   902  		},
   903  		CodeSection: []wasm.Code{
   904  			{ // f1
   905  				Body: []byte{
   906  					wasm.OpcodeI32Const, 42,
   907  					wasm.OpcodeGlobalSet, 0, // store 42 in global 0
   908  					wasm.OpcodeI32Const, 11,
   909  					wasm.OpcodeGlobalSet, 1, // store 11 in global 1
   910  					wasm.OpcodeI32Const, 0, // reserve return for f2
   911  					wasm.OpcodeCall, 1, // call f2
   912  					wasm.OpcodeEnd,
   913  				},
   914  			},
   915  			{ // f2
   916  				LocalTypes: []wasm.ValueType{wasm.ValueTypeI32},
   917  				Body: []byte{
   918  					wasm.OpcodeI32Const, 42, // local for f2
   919  					wasm.OpcodeLocalSet, 0,
   920  					wasm.OpcodeI32Const, 6,
   921  					wasm.OpcodeCall, 2, // call host function
   922  					wasm.OpcodeEnd,
   923  				},
   924  			},
   925  			// f3
   926  			hostgofn,
   927  		},
   928  		ExportSection: []wasm.Export{
   929  			{Name: "f1", Type: wasm.ExternTypeFunc, Index: 0},
   930  			{Name: "f2", Type: wasm.ExternTypeFunc, Index: 1},
   931  			{Name: "f3", Type: wasm.ExternTypeFunc, Index: 2},
   932  		},
   933  		ID: wasm.ModuleID{0},
   934  	}
   935  
   936  	f1offset := uint64(0)
   937  	f2offset := f1offset + uint64(len(m.CodeSection[0].Body))
   938  	f3offset := f2offset + uint64(len(m.CodeSection[1].Body))
   939  	m.CodeSection[0].BodyOffsetInCodeSection = f1offset
   940  	m.CodeSection[1].BodyOffsetInCodeSection = f2offset
   941  
   942  	listeners := buildFunctionListeners(fnListener, m)
   943  	err = e.CompileModule(testCtx, m, listeners, false)
   944  	require.NoError(t, err)
   945  
   946  	module := &wasm.ModuleInstance{
   947  		ModuleName: t.Name(),
   948  		TypeIDs:    []wasm.FunctionTypeID{0, 1, 2},
   949  		Exports:    exportMap(m),
   950  		Globals: []*wasm.GlobalInstance{
   951  			{Val: 100, Type: wasm.GlobalType{ValType: wasm.ValueTypeI32, Mutable: true}},
   952  			{Val: 200, Type: wasm.GlobalType{ValType: wasm.ValueTypeI32, Mutable: true}},
   953  		},
   954  		Source: m,
   955  	}
   956  
   957  	me, err := e.NewModuleEngine(m, module)
   958  	require.NoError(t, err)
   959  	linkModuleToEngine(module, me)
   960  
   961  	initCallEngine := me.NewFunction(0) // f1
   962  	_, err = initCallEngine.Call(testCtx, 2, 3, 4)
   963  	require.NoError(t, err)
   964  
   965  	defs := module.ExportedFunctionDefinitions()
   966  	f1 := defs["f1"]
   967  	f2 := defs["f2"]
   968  	f3 := defs["f3"]
   969  	t.Logf("f1 offset: %#x", f1offset)
   970  	t.Logf("f2 offset: %#x", f2offset)
   971  	t.Logf("f3 offset: %#x", f3offset)
   972  
   973  	expectedStacks := [][]frame{
   974  		{
   975  			{f1, f1offset + 0},
   976  		},
   977  		{
   978  			{f2, f2offset + 0},
   979  			{f1, f1offset + 10}, // index of call opcode in f1's code
   980  		},
   981  		{
   982  			{f3, 0},             // host functions don't have a wasm code offset
   983  			{f2, f2offset + 6},  // index of call opcode in f2's code
   984  			{f1, f1offset + 10}, // index of call opcode in f1's code
   985  		},
   986  	}
   987  
   988  	for si, stack := range tape {
   989  		t.Log("Recorded stack", si, ":")
   990  		require.True(t, len(expectedStacks) > 0, "more recorded stacks than expected stacks")
   991  		expectedStack := expectedStacks[0]
   992  		expectedStacks = expectedStacks[1:]
   993  		for fi, frame := range stack {
   994  			t.Logf("\t%d -> %s :: %#x", fi, frame.function.Name(), frame.offset)
   995  			require.True(t, len(expectedStack) > 0, "more frames in stack than expected")
   996  			expectedFrame := expectedStack[0]
   997  			expectedStack = expectedStack[1:]
   998  			require.Equal(t, expectedFrame, frame)
   999  		}
  1000  		require.Zero(t, len(expectedStack), "expected more frames in stack")
  1001  	}
  1002  	require.Zero(t, len(expectedStacks), "expected more stacks")
  1003  }
  1004  
  1005  // RunTestModuleEngineMemory shows that the byte slice returned from api.Memory Read is not a copy, rather a re-slice
  1006  // of the underlying memory. This allows both host and Wasm to see each other's writes, unless one side changes the
  1007  // capacity of the slice.
  1008  //
  1009  // Known cases that change the slice capacity:
  1010  // * Host code calls append on a byte slice returned by api.Memory Read
  1011  // * Wasm code calls wasm.OpcodeMemoryGrowName and this changes the capacity (by default, it will).
  1012  func RunTestModuleEngineMemory(t *testing.T, et EngineTester) {
  1013  	e := et.NewEngine(api.CoreFeaturesV2)
  1014  
  1015  	wasmPhrase := "Well, that'll be the day when you say goodbye."
  1016  	wasmPhraseSize := uint32(len(wasmPhrase))
  1017  
  1018  	// Define a basic function which defines one parameter. This is used to test results when incorrect arity is used.
  1019  	one := uint32(1)
  1020  	m := &wasm.Module{
  1021  		TypeSection:     []wasm.FunctionType{{Params: []api.ValueType{api.ValueTypeI32}, ParamNumInUint64: 1}, {}},
  1022  		FunctionSection: []wasm.Index{0, 1},
  1023  		MemorySection:   &wasm.Memory{Min: 1, Cap: 1, Max: 2},
  1024  		DataSection: []wasm.DataSegment{
  1025  			{
  1026  				Passive: true,
  1027  				Init:    []byte(wasmPhrase),
  1028  			},
  1029  		},
  1030  		DataCountSection: &one,
  1031  		CodeSection: []wasm.Code{
  1032  			{Body: []byte{ // "grow"
  1033  				wasm.OpcodeLocalGet, 0, // how many pages to grow (param)
  1034  				wasm.OpcodeMemoryGrow, 0, // memory index zero
  1035  				wasm.OpcodeDrop, // drop the previous page count (or -1 if grow failed)
  1036  				wasm.OpcodeEnd,
  1037  			}},
  1038  			{Body: []byte{ // "init"
  1039  				wasm.OpcodeI32Const, 0, // target offset
  1040  				wasm.OpcodeI32Const, 0, // source offset
  1041  				wasm.OpcodeI32Const, byte(wasmPhraseSize), // len
  1042  				wasm.OpcodeMiscPrefix, wasm.OpcodeMiscMemoryInit, 0, 0, // segment 0, memory 0
  1043  				wasm.OpcodeEnd,
  1044  			}},
  1045  		},
  1046  		ExportSection: []wasm.Export{
  1047  			{Name: "grow", Type: wasm.ExternTypeFunc, Index: 0},
  1048  			{Name: "init", Type: wasm.ExternTypeFunc, Index: 1},
  1049  		},
  1050  	}
  1051  	listeners := buildFunctionListeners(et.ListenerFactory(), m)
  1052  
  1053  	err := e.CompileModule(testCtx, m, listeners, false)
  1054  	require.NoError(t, err)
  1055  
  1056  	// Assign memory to the module instance
  1057  	module := &wasm.ModuleInstance{
  1058  		ModuleName:     t.Name(),
  1059  		MemoryInstance: wasm.NewMemoryInstance(m.MemorySection),
  1060  		DataInstances:  []wasm.DataInstance{m.DataSection[0].Init},
  1061  		TypeIDs:        []wasm.FunctionTypeID{0, 1},
  1062  	}
  1063  	memory := module.MemoryInstance
  1064  
  1065  	// To use functions, we need to instantiate them (associate them with a ModuleInstance).
  1066  	module.Exports = exportMap(m)
  1067  	const grow, init = 0, 1
  1068  
  1069  	// Compile the module
  1070  	me, err := e.NewModuleEngine(m, module)
  1071  	require.NoError(t, err)
  1072  	linkModuleToEngine(module, me)
  1073  
  1074  	buf, ok := memory.Read(0, wasmPhraseSize)
  1075  	require.True(t, ok)
  1076  	require.Equal(t, make([]byte, wasmPhraseSize), buf)
  1077  
  1078  	// Initialize the memory using Wasm. This copies the test phrase.
  1079  	initCallEngine := me.NewFunction(init)
  1080  	_, err = initCallEngine.Call(testCtx)
  1081  	require.NoError(t, err)
  1082  
  1083  	// We expect the same []byte read earlier to now include the phrase in wasm.
  1084  	require.Equal(t, wasmPhrase, string(buf))
  1085  
  1086  	hostPhrase := "Goodbye, cruel world. I'm off to join the circus." // Intentionally slightly longer.
  1087  	hostPhraseSize := uint32(len(hostPhrase))
  1088  
  1089  	// Copy over the buffer, which should stop at the current length.
  1090  	copy(buf, hostPhrase)
  1091  	require.Equal(t, "Goodbye, cruel world. I'm off to join the circ", string(buf))
  1092  
  1093  	// The underlying memory should be updated. This proves that Memory.Read returns a re-slice, not a copy, and that
  1094  	// programs can rely on this (for example, to update shared state in Wasm and view that in Go and visa versa).
  1095  	buf2, ok := memory.Read(0, wasmPhraseSize)
  1096  	require.True(t, ok)
  1097  	require.Equal(t, buf, buf2)
  1098  
  1099  	// Now, append to the buffer we got from Wasm. As this changes capacity, it should result in a new byte slice.
  1100  	buf = append(buf, 'u', 's', '.')
  1101  	require.Equal(t, hostPhrase, string(buf))
  1102  
  1103  	// To prove the above, we re-read the memory and should not see the appended bytes (rather zeros instead).
  1104  	buf2, ok = memory.Read(0, hostPhraseSize)
  1105  	require.True(t, ok)
  1106  	hostPhraseTruncated := "Goodbye, cruel world. I'm off to join the circ" + string([]byte{0, 0, 0})
  1107  	require.Equal(t, hostPhraseTruncated, string(buf2))
  1108  
  1109  	// Now, we need to prove the other direction, that when Wasm changes the capacity, the host's buffer is unaffected.
  1110  	growCallEngine := me.NewFunction(grow)
  1111  	_, err = growCallEngine.Call(testCtx, 1)
  1112  	require.NoError(t, err)
  1113  
  1114  	// The host buffer should still contain the same bytes as before grow
  1115  	require.Equal(t, hostPhraseTruncated, string(buf2))
  1116  
  1117  	// Re-initialize the memory in wasm, which overwrites the region.
  1118  	initCallEngine2 := me.NewFunction(init)
  1119  	_, err = initCallEngine2.Call(testCtx)
  1120  	require.NoError(t, err)
  1121  
  1122  	// The host was not affected because it is a different slice due to "memory.grow" affecting the underlying memory.
  1123  	require.Equal(t, hostPhraseTruncated, string(buf2))
  1124  }
  1125  
  1126  const (
  1127  	divByWasmName             = "div_by.wasm"
  1128  	divByGoName               = "div_by.go"
  1129  	callDivByGoName           = "call->" + divByGoName
  1130  	callImportCallDivByGoName = "call_import->" + callDivByGoName
  1131  )
  1132  
  1133  func divByGo(d uint32) uint32 {
  1134  	if d == math.MaxUint32 {
  1135  		panic(errors.New("host-function panic"))
  1136  	}
  1137  	return 1 / d // go panics if d == 0
  1138  }
  1139  
  1140  var hostDivByGo = wasm.MustParseGoReflectFuncCode(divByGo)
  1141  
  1142  // (func (export "div_by.wasm") (param i32) (result i32) (i32.div_u (i32.const 1) (local.get 0)))
  1143  var (
  1144  	divByWasm     = []byte{wasm.OpcodeI32Const, 1, wasm.OpcodeLocalGet, 0, wasm.OpcodeI32DivU, wasm.OpcodeEnd}
  1145  	hostDivByWasm = &wasm.Code{Body: divByWasm}
  1146  )
  1147  
  1148  const (
  1149  	readMemName           = "read_mem"
  1150  	callImportReadMemName = "call_import->read_mem"
  1151  )
  1152  
  1153  func readMemGo(_ context.Context, m api.Module) uint64 {
  1154  	ret, ok := m.Memory().ReadUint64Le(0)
  1155  	if !ok {
  1156  		panic("couldn't read memory")
  1157  	}
  1158  	return ret
  1159  }
  1160  
  1161  var hostReadMemGo = wasm.MustParseGoReflectFuncCode(readMemGo)
  1162  
  1163  func setupCallTests(t *testing.T, e wasm.Engine, divBy *wasm.Code, fnlf experimental.FunctionListenerFactory) (*wasm.ModuleInstance, *wasm.ModuleInstance) {
  1164  	ft := wasm.FunctionType{Params: []wasm.ValueType{i32}, Results: []wasm.ValueType{i32}, ParamNumInUint64: 1, ResultNumInUint64: 1}
  1165  
  1166  	divByName := divByWasmName
  1167  	if divBy.GoFunc != nil {
  1168  		divByName = divByGoName
  1169  	}
  1170  	hostModule := &wasm.Module{
  1171  		TypeSection:     []wasm.FunctionType{ft},
  1172  		FunctionSection: []wasm.Index{0},
  1173  		CodeSection:     []wasm.Code{*divBy},
  1174  		ExportSection:   []wasm.Export{{Name: divByGoName, Type: wasm.ExternTypeFunc, Index: 0}},
  1175  		NameSection: &wasm.NameSection{
  1176  			ModuleName:    "host",
  1177  			FunctionNames: wasm.NameMap{{Index: wasm.Index(0), Name: divByName}},
  1178  		},
  1179  		ID: wasm.ModuleID{0},
  1180  	}
  1181  	lns := buildFunctionListeners(fnlf, hostModule)
  1182  	err := e.CompileModule(testCtx, hostModule, lns, false)
  1183  	require.NoError(t, err)
  1184  	host := &wasm.ModuleInstance{ModuleName: hostModule.NameSection.ModuleName, TypeIDs: []wasm.FunctionTypeID{0}}
  1185  	host.Exports = exportMap(hostModule)
  1186  
  1187  	hostME, err := e.NewModuleEngine(hostModule, host)
  1188  	require.NoError(t, err)
  1189  	linkModuleToEngine(host, hostME)
  1190  
  1191  	importedModule := &wasm.Module{
  1192  		ImportFunctionCount: 1,
  1193  		ImportSection:       []wasm.Import{{}},
  1194  		TypeSection:         []wasm.FunctionType{ft},
  1195  		FunctionSection:     []wasm.Index{0, 0},
  1196  		CodeSection: []wasm.Code{
  1197  			{Body: divByWasm},
  1198  			{Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeCall, byte(0), // Calling imported host function ^.
  1199  				wasm.OpcodeEnd}},
  1200  		},
  1201  		ExportSection: []wasm.Export{
  1202  			{Name: divByWasmName, Type: wasm.ExternTypeFunc, Index: 1},
  1203  			{Name: callDivByGoName, Type: wasm.ExternTypeFunc, Index: 2},
  1204  		},
  1205  		NameSection: &wasm.NameSection{
  1206  			ModuleName: "imported",
  1207  			FunctionNames: wasm.NameMap{
  1208  				{Index: wasm.Index(1), Name: divByWasmName},
  1209  				{Index: wasm.Index(2), Name: callDivByGoName},
  1210  			},
  1211  		},
  1212  		ID: wasm.ModuleID{1},
  1213  	}
  1214  	lns = buildFunctionListeners(fnlf, importedModule)
  1215  	err = e.CompileModule(testCtx, importedModule, lns, false)
  1216  	require.NoError(t, err)
  1217  
  1218  	imported := &wasm.ModuleInstance{
  1219  		ModuleName: importedModule.NameSection.ModuleName, TypeIDs: []wasm.FunctionTypeID{0},
  1220  	}
  1221  	imported.Exports = exportMap(importedModule)
  1222  
  1223  	// Compile the imported module
  1224  	importedMe, err := e.NewModuleEngine(importedModule, imported)
  1225  	require.NoError(t, err)
  1226  	linkModuleToEngine(imported, importedMe)
  1227  	importedMe.ResolveImportedFunction(0, 0, hostME)
  1228  
  1229  	// To test stack traces, call the same function from another module
  1230  	importingModule := &wasm.Module{
  1231  		ImportFunctionCount: 1,
  1232  		TypeSection:         []wasm.FunctionType{ft},
  1233  		ImportSection:       []wasm.Import{{}},
  1234  		FunctionSection:     []wasm.Index{0},
  1235  		CodeSection: []wasm.Code{
  1236  			{Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeCall, 0 /* only one imported function */, wasm.OpcodeEnd}},
  1237  		},
  1238  		ExportSection: []wasm.Export{
  1239  			{Name: callImportCallDivByGoName, Type: wasm.ExternTypeFunc, Index: 1},
  1240  		},
  1241  		NameSection: &wasm.NameSection{
  1242  			ModuleName:    "importing",
  1243  			FunctionNames: wasm.NameMap{{Index: wasm.Index(1), Name: callImportCallDivByGoName}},
  1244  		},
  1245  		ID: wasm.ModuleID{2},
  1246  	}
  1247  	lns = buildFunctionListeners(fnlf, importingModule)
  1248  	err = e.CompileModule(testCtx, importingModule, lns, false)
  1249  	require.NoError(t, err)
  1250  
  1251  	// Add the exported function.
  1252  	importing := &wasm.ModuleInstance{ModuleName: importingModule.NameSection.ModuleName, TypeIDs: []wasm.FunctionTypeID{0}}
  1253  	importing.Exports = exportMap(importingModule)
  1254  
  1255  	// Compile the importing module
  1256  	importingMe, err := e.NewModuleEngine(importingModule, importing)
  1257  	require.NoError(t, err)
  1258  	linkModuleToEngine(importing, importingMe)
  1259  	importingMe.ResolveImportedFunction(0, 2, importedMe)
  1260  	return imported, importing
  1261  }
  1262  
  1263  func setupCallMemTests(t *testing.T, e wasm.Engine, readMem *wasm.Code) *wasm.ModuleInstance {
  1264  	ft := wasm.FunctionType{Results: []wasm.ValueType{i64}, ResultNumInUint64: 1}
  1265  
  1266  	hostModule := &wasm.Module{
  1267  		TypeSection:     []wasm.FunctionType{ft},
  1268  		FunctionSection: []wasm.Index{0},
  1269  		CodeSection:     []wasm.Code{*readMem},
  1270  		ExportSection: []wasm.Export{
  1271  			{Name: readMemName, Type: wasm.ExternTypeFunc, Index: 0},
  1272  		},
  1273  		NameSection: &wasm.NameSection{
  1274  			ModuleName:    "host",
  1275  			FunctionNames: wasm.NameMap{{Index: 0, Name: readMemName}},
  1276  		},
  1277  		ID: wasm.ModuleID{0},
  1278  	}
  1279  	err := e.CompileModule(testCtx, hostModule, nil, false)
  1280  	require.NoError(t, err)
  1281  	host := &wasm.ModuleInstance{ModuleName: hostModule.NameSection.ModuleName, TypeIDs: []wasm.FunctionTypeID{0}}
  1282  	host.Exports = exportMap(hostModule)
  1283  
  1284  	hostMe, err := e.NewModuleEngine(hostModule, host)
  1285  	require.NoError(t, err)
  1286  	linkModuleToEngine(host, hostMe)
  1287  
  1288  	importingModule := &wasm.Module{
  1289  		ImportFunctionCount: 1,
  1290  		TypeSection:         []wasm.FunctionType{ft},
  1291  		ImportSection: []wasm.Import{
  1292  			// Placeholder for two import functions from `importedModule`.
  1293  			{Type: wasm.ExternTypeFunc, DescFunc: 0},
  1294  		},
  1295  		FunctionSection: []wasm.Index{0},
  1296  		ExportSection: []wasm.Export{
  1297  			{Name: callImportReadMemName, Type: wasm.ExternTypeFunc, Index: 1},
  1298  		},
  1299  		CodeSection: []wasm.Code{
  1300  			{Body: []byte{wasm.OpcodeCall, 0, wasm.OpcodeEnd}}, // Calling the index 1 = readMemFn.
  1301  		},
  1302  		NameSection: &wasm.NameSection{
  1303  			ModuleName: "importing",
  1304  			FunctionNames: wasm.NameMap{
  1305  				{Index: 2, Name: callImportReadMemName},
  1306  			},
  1307  		},
  1308  		// Indicates that this module has a memory so that compilers are able to assembe memory-related initialization.
  1309  		MemorySection: &wasm.Memory{Min: 1},
  1310  		ID:            wasm.ModuleID{1},
  1311  	}
  1312  	err = e.CompileModule(testCtx, importingModule, nil, false)
  1313  	require.NoError(t, err)
  1314  
  1315  	// Add the exported function.
  1316  	importing := &wasm.ModuleInstance{ModuleName: importingModule.NameSection.ModuleName, TypeIDs: []wasm.FunctionTypeID{0}}
  1317  	// Note: adds imported functions readMemFn and callReadMemFn at index 0 and 1.
  1318  	importing.Exports = exportMap(importingModule)
  1319  
  1320  	// Compile the importing module
  1321  	importingMe, err := e.NewModuleEngine(importingModule, importing)
  1322  	require.NoError(t, err)
  1323  	linkModuleToEngine(importing, importingMe)
  1324  	importingMe.ResolveImportedFunction(0, 0, hostMe)
  1325  	return importing
  1326  }
  1327  
  1328  // linkModuleToEngine assigns fields that wasm.Store would on instantiation. These include fields both interpreter and
  1329  // Compiler needs as well as fields only needed by Compiler.
  1330  //
  1331  // Note: This sets fields that are not needed in the interpreter, but are required by code compiled by Compiler. If a new
  1332  // test here passes in the interpreter and segmentation faults in Compiler, check for a new field offset or a change in Compiler
  1333  // (e.g. compiler.TestVerifyOffsetValue). It is possible for all other tests to pass as that field is implicitly set by
  1334  // wasm.Store: store isn't used here for unit test precision.
  1335  func linkModuleToEngine(module *wasm.ModuleInstance, me wasm.ModuleEngine) {
  1336  	module.Engine = me // for Compiler, links the module to the module-engine compiled from it (moduleInstanceEngineOffset).
  1337  }
  1338  
  1339  func buildFunctionListeners(factory experimental.FunctionListenerFactory, m *wasm.Module) []experimental.FunctionListener {
  1340  	if factory == nil || len(m.FunctionSection) == 0 {
  1341  		return nil
  1342  	}
  1343  	listeners := make([]experimental.FunctionListener, len(m.FunctionSection))
  1344  	importCount := m.ImportFunctionCount
  1345  	for i := 0; i < len(listeners); i++ {
  1346  		listeners[i] = factory.NewFunctionListener(m.FunctionDefinition(uint32(i) + importCount))
  1347  	}
  1348  	return listeners
  1349  }
  1350  
  1351  func exportMap(m *wasm.Module) map[string]*wasm.Export {
  1352  	ret := make(map[string]*wasm.Export, len(m.ExportSection))
  1353  	for i := range m.ExportSection {
  1354  		exp := &m.ExportSection[i]
  1355  		ret[exp.Name] = exp
  1356  	}
  1357  	return ret
  1358  }