github.com/tetratelabs/wazero@v1.7.1/internal/engine/wazevo/engine_test.go (about)

     1  package wazevo
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  	"testing"
     8  	"unsafe"
     9  
    10  	"github.com/tetratelabs/wazero/internal/platform"
    11  	"github.com/tetratelabs/wazero/internal/testing/require"
    12  	"github.com/tetratelabs/wazero/internal/wasm"
    13  )
    14  
    15  func Test_sharedFunctionsFinalizer(t *testing.T) {
    16  	sf := &sharedFunctions{}
    17  
    18  	b1, err := platform.MmapCodeSegment(100)
    19  	require.NoError(t, err)
    20  	b2, err := platform.MmapCodeSegment(100)
    21  	require.NoError(t, err)
    22  	b3, err := platform.MmapCodeSegment(100)
    23  	require.NoError(t, err)
    24  	b6, err := platform.MmapCodeSegment(100)
    25  	require.NoError(t, err)
    26  	b7, err := platform.MmapCodeSegment(100)
    27  	require.NoError(t, err)
    28  	b8, err := platform.MmapCodeSegment(100)
    29  	require.NoError(t, err)
    30  	b9, err := platform.MmapCodeSegment(100)
    31  	require.NoError(t, err)
    32  	b10, err := platform.MmapCodeSegment(100)
    33  	require.NoError(t, err)
    34  
    35  	sf.memoryGrowExecutable = b1
    36  	sf.stackGrowExecutable = b2
    37  	sf.checkModuleExitCode = b3
    38  	sf.tableGrowExecutable = b6
    39  	sf.refFuncExecutable = b7
    40  	sf.memoryWait32Executable = b8
    41  	sf.memoryWait64Executable = b9
    42  	sf.memoryNotifyExecutable = b10
    43  
    44  	sharedFunctionsFinalizer(sf)
    45  	require.Nil(t, sf.memoryGrowExecutable)
    46  	require.Nil(t, sf.stackGrowExecutable)
    47  	require.Nil(t, sf.checkModuleExitCode)
    48  	require.Nil(t, sf.tableGrowExecutable)
    49  	require.Nil(t, sf.refFuncExecutable)
    50  	require.Nil(t, sf.memoryWait32Executable)
    51  	require.Nil(t, sf.memoryWait64Executable)
    52  	require.Nil(t, sf.memoryNotifyExecutable)
    53  }
    54  
    55  func Test_executablesFinalizer(t *testing.T) {
    56  	b, err := platform.MmapCodeSegment(100)
    57  	require.NoError(t, err)
    58  
    59  	exec := &executables{}
    60  	exec.executable = b
    61  	executablesFinalizer(exec)
    62  	require.Nil(t, exec.executable)
    63  }
    64  
    65  type fakeFinalizer map[*executables]func(module *executables)
    66  
    67  func (f fakeFinalizer) setFinalizer(obj interface{}, finalizer interface{}) {
    68  	cf := obj.(*executables)
    69  	if _, ok := f[cf]; ok { // easier than adding a field for testing.T
    70  		panic(fmt.Sprintf("BUG: %v already had its finalizer set", cf))
    71  	}
    72  	f[cf] = finalizer.(func(*executables))
    73  }
    74  
    75  func TestEngine_CompileModule(t *testing.T) {
    76  	ctx := context.Background()
    77  	e := NewEngine(ctx, 0, nil).(*engine)
    78  	ff := fakeFinalizer{}
    79  	e.setFinalizer = ff.setFinalizer
    80  
    81  	okModule := &wasm.Module{
    82  		TypeSection:     []wasm.FunctionType{{}},
    83  		FunctionSection: []wasm.Index{0, 0, 0, 0},
    84  		CodeSection: []wasm.Code{
    85  			{Body: []byte{wasm.OpcodeEnd}},
    86  			{Body: []byte{wasm.OpcodeEnd}},
    87  			{Body: []byte{wasm.OpcodeEnd}},
    88  			{Body: []byte{wasm.OpcodeEnd}},
    89  		},
    90  		ID: wasm.ModuleID{},
    91  	}
    92  
    93  	err := e.CompileModule(ctx, okModule, nil, false)
    94  	require.NoError(t, err)
    95  
    96  	// Compiling same module shouldn't be compiled again, but instead should be cached.
    97  	err = e.CompileModule(ctx, okModule, nil, false)
    98  	require.NoError(t, err)
    99  
   100  	// Pretend the finalizer executed, by invoking them one-by-one.
   101  	for k, v := range ff {
   102  		v(k)
   103  	}
   104  }
   105  
   106  func TestEngine_sortedCompiledModules(t *testing.T) {
   107  	getCM := func(addr uintptr) *compiledModule {
   108  		var buf []byte
   109  		{
   110  			// TODO: use unsafe.Slice after floor version is set to Go 1.20.
   111  			hdr := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
   112  			hdr.Data = addr
   113  			hdr.Len = 4
   114  			hdr.Cap = 4
   115  		}
   116  		cm := &compiledModule{executables: &executables{executable: buf}}
   117  		return cm
   118  	}
   119  
   120  	requireEqualExisting := func(t *testing.T, e *engine, expected []uintptr) {
   121  		actual := make([]uintptr, 0)
   122  		for _, cm := range e.sortedCompiledModules {
   123  			actual = append(actual, uintptr(unsafe.Pointer(&cm.executable[0])))
   124  		}
   125  		require.Equal(t, expected, actual)
   126  	}
   127  
   128  	m1 := getCM(1)
   129  	m100 := getCM(100)
   130  	m5 := getCM(5)
   131  	m10 := getCM(10)
   132  
   133  	t.Run("add", func(t *testing.T) {
   134  		e := &engine{}
   135  		e.addCompiledModuleToSortedList(m1)
   136  		e.addCompiledModuleToSortedList(m100)
   137  		e.addCompiledModuleToSortedList(m5)
   138  		e.addCompiledModuleToSortedList(m10)
   139  		requireEqualExisting(t, e, []uintptr{1, 5, 10, 100})
   140  	})
   141  	t.Run("delete", func(t *testing.T) {
   142  		e := &engine{}
   143  		e.addCompiledModuleToSortedList(m1)
   144  		e.addCompiledModuleToSortedList(m100)
   145  		e.addCompiledModuleToSortedList(m5)
   146  		e.addCompiledModuleToSortedList(m10)
   147  		e.deleteCompiledModuleFromSortedList(m100)
   148  		require.Equal(t, 3, len(e.sortedCompiledModules))
   149  		requireEqualExisting(t, e, []uintptr{1, 5, 10})
   150  		e.deleteCompiledModuleFromSortedList(m1)
   151  		requireEqualExisting(t, e, []uintptr{5, 10})
   152  		e.deleteCompiledModuleFromSortedList(m10)
   153  		requireEqualExisting(t, e, []uintptr{5})
   154  		e.deleteCompiledModuleFromSortedList(m5)
   155  		requireEqualExisting(t, e, []uintptr{})
   156  	})
   157  
   158  	t.Run("OfAddr", func(t *testing.T) {
   159  		e := &engine{}
   160  		e.addCompiledModuleToSortedList(m1)
   161  		e.addCompiledModuleToSortedList(m100)
   162  		e.addCompiledModuleToSortedList(m5)
   163  		e.addCompiledModuleToSortedList(m10)
   164  
   165  		require.Equal(t, nil, e.compiledModuleOfAddr(0))
   166  		require.Equal(t, unsafe.Pointer(m1), unsafe.Pointer(e.compiledModuleOfAddr(1)))
   167  		require.Equal(t, unsafe.Pointer(m1), unsafe.Pointer(e.compiledModuleOfAddr(4)))
   168  		require.Equal(t, unsafe.Pointer(m5), unsafe.Pointer(e.compiledModuleOfAddr(5)))
   169  		require.Equal(t, unsafe.Pointer(m5), unsafe.Pointer(e.compiledModuleOfAddr(8)))
   170  		require.Equal(t, unsafe.Pointer(m10), unsafe.Pointer(e.compiledModuleOfAddr(10)))
   171  		require.Equal(t, unsafe.Pointer(m10), unsafe.Pointer(e.compiledModuleOfAddr(11)))
   172  		require.Equal(t, unsafe.Pointer(m10), unsafe.Pointer(e.compiledModuleOfAddr(12)))
   173  		require.Equal(t, unsafe.Pointer(m100), unsafe.Pointer(e.compiledModuleOfAddr(100)))
   174  		require.Equal(t, unsafe.Pointer(m100), unsafe.Pointer(e.compiledModuleOfAddr(103)))
   175  		e.deleteCompiledModuleFromSortedList(m1)
   176  		require.Equal(t, nil, e.compiledModuleOfAddr(1))
   177  		require.Equal(t, nil, e.compiledModuleOfAddr(2))
   178  		require.Equal(t, nil, e.compiledModuleOfAddr(4))
   179  		e.deleteCompiledModuleFromSortedList(m100)
   180  		require.Equal(t, nil, e.compiledModuleOfAddr(100))
   181  		require.Equal(t, nil, e.compiledModuleOfAddr(103))
   182  	})
   183  }
   184  
   185  func TestCompiledModule_functionIndexOf(t *testing.T) {
   186  	const executableAddr = 0xaaaa
   187  	var executable []byte
   188  	{
   189  		// TODO: use unsafe.Slice after floor version is set to Go 1.20.
   190  		hdr := (*reflect.SliceHeader)(unsafe.Pointer(&executable))
   191  		hdr.Data = executableAddr
   192  		hdr.Len = 0xffff
   193  		hdr.Cap = 0xffff
   194  	}
   195  
   196  	cm := &compiledModule{
   197  		executables:     &executables{executable: executable},
   198  		functionOffsets: []int{0, 500, 1000, 2000},
   199  	}
   200  	require.Equal(t, wasm.Index(0), cm.functionIndexOf(executableAddr))
   201  	require.Equal(t, wasm.Index(0), cm.functionIndexOf(executableAddr+499))
   202  	require.Equal(t, wasm.Index(1), cm.functionIndexOf(executableAddr+500))
   203  	require.Equal(t, wasm.Index(1), cm.functionIndexOf(executableAddr+999))
   204  	require.Equal(t, wasm.Index(2), cm.functionIndexOf(executableAddr+1000))
   205  	require.Equal(t, wasm.Index(2), cm.functionIndexOf(executableAddr+1500))
   206  	require.Equal(t, wasm.Index(2), cm.functionIndexOf(executableAddr+1999))
   207  	require.Equal(t, wasm.Index(3), cm.functionIndexOf(executableAddr+2000))
   208  }
   209  
   210  func Test_checkAddrInBytes(t *testing.T) {
   211  	bytes := []byte{0, 1, 2, 3, 4, 5, 6, 7}
   212  	begin := uintptr(unsafe.Pointer(&bytes[0]))
   213  	end := uintptr(unsafe.Pointer(&bytes[len(bytes)-1]))
   214  
   215  	require.True(t, checkAddrInBytes(begin, bytes))
   216  	require.True(t, checkAddrInBytes(end, bytes))
   217  	require.False(t, checkAddrInBytes(begin-1, bytes))
   218  	require.False(t, checkAddrInBytes(end+1, bytes))
   219  }