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