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 }