github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/engine/wazevo/engine_cache_test.go (about) 1 package wazevo 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 "hash/crc32" 7 "io" 8 "testing" 9 10 "github.com/tetratelabs/wazero/internal/testing/require" 11 "github.com/tetratelabs/wazero/internal/u32" 12 "github.com/tetratelabs/wazero/internal/u64" 13 "github.com/tetratelabs/wazero/internal/wasm" 14 ) 15 16 var testVersion = "0.0.1" 17 18 func crcf(b []byte) []byte { 19 c := crc32.Checksum(b, crc) 20 return u32.LeBytes(c) 21 } 22 23 func TestSerializeCompiledModule(t *testing.T) { 24 tests := []struct { 25 in *compiledModule 26 exp []byte 27 }{ 28 { 29 in: &compiledModule{ 30 executables: &executables{executable: []byte{1, 2, 3, 4, 5}}, 31 functionOffsets: []int{0}, 32 }, 33 exp: concat( 34 magic, 35 []byte{byte(len(testVersion))}, 36 []byte(testVersion), 37 u32.LeBytes(1), // number of functions. 38 u64.LeBytes(0), // offset. 39 u64.LeBytes(5), // length of code. 40 []byte{1, 2, 3, 4, 5}, // code. 41 crcf([]byte{1, 2, 3, 4, 5}), // crc for the code. 42 []byte{0}, // no source map. 43 ), 44 }, 45 { 46 in: &compiledModule{ 47 executables: &executables{executable: []byte{1, 2, 3, 4, 5}}, 48 functionOffsets: []int{0}, 49 }, 50 exp: concat( 51 magic, 52 []byte{byte(len(testVersion))}, 53 []byte(testVersion), 54 u32.LeBytes(1), // number of functions. 55 u64.LeBytes(0), // offset. 56 u64.LeBytes(5), // length of code. 57 []byte{1, 2, 3, 4, 5}, // code. 58 crcf([]byte{1, 2, 3, 4, 5}), // crc for the code. 59 []byte{0}, // no source map. 60 ), 61 }, 62 { 63 in: &compiledModule{ 64 executables: &executables{executable: []byte{1, 2, 3, 4, 5, 1, 2, 3}}, 65 functionOffsets: []int{0, 5}, 66 }, 67 exp: concat( 68 magic, 69 []byte{byte(len(testVersion))}, 70 []byte(testVersion), 71 u32.LeBytes(2), // number of functions. 72 // Function index = 0. 73 u64.LeBytes(0), // offset. 74 // Function index = 1. 75 u64.LeBytes(5), // offset. 76 // Executable. 77 u64.LeBytes(8), // length of code. 78 []byte{1, 2, 3, 4, 5, 1, 2, 3}, // code. 79 crcf([]byte{1, 2, 3, 4, 5, 1, 2, 3}), // crc for the code. 80 []byte{0}, // no source map. 81 ), 82 }, 83 } 84 85 for i, tc := range tests { 86 actual, err := io.ReadAll(serializeCompiledModule(testVersion, tc.in)) 87 require.NoError(t, err, i) 88 require.Equal(t, tc.exp, actual, i) 89 } 90 } 91 92 func concat(ins ...[]byte) (ret []byte) { 93 for _, in := range ins { 94 ret = append(ret, in...) 95 } 96 return 97 } 98 99 func TestDeserializeCompiledModule(t *testing.T) { 100 tests := []struct { 101 name string 102 in []byte 103 importedFunctionCount uint32 104 expCompiledModule *compiledModule 105 expStaleCache bool 106 expErr string 107 }{ 108 { 109 name: "invalid header", 110 in: []byte{1}, 111 expErr: "compilationcache: invalid header length: 1", 112 }, 113 { 114 name: "invalid magic", 115 in: concat( 116 []byte{'a', 'b', 'c', 'd', 'e', 'f'}, 117 []byte{byte(len(testVersion))}, 118 []byte(testVersion), 119 u32.LeBytes(1), // number of functions. 120 ), 121 expErr: "compilationcache: invalid magic number: got WAZEVO but want abcdef", 122 }, 123 { 124 name: "version mismatch", 125 in: concat( 126 magic, 127 []byte{byte(len("1233123.1.1"))}, 128 []byte("1233123.1.1"), 129 u32.LeBytes(1), // number of functions. 130 ), 131 expStaleCache: true, 132 }, 133 { 134 name: "version mismatch", 135 in: concat( 136 magic, 137 []byte{byte(len("0.0.0"))}, 138 []byte("0.0.0"), 139 u32.LeBytes(1), // number of functions. 140 ), 141 expStaleCache: true, 142 }, 143 { 144 name: "one function", 145 in: concat( 146 magic, 147 []byte{byte(len(testVersion))}, 148 []byte(testVersion), 149 u32.LeBytes(1), // number of functions. 150 u64.LeBytes(0), // offset. 151 // Executable. 152 u64.LeBytes(5), // size. 153 []byte{1, 2, 3, 4, 5}, // machine code. 154 crcf([]byte{1, 2, 3, 4, 5}), // machine code. 155 []byte{0}, // no source map. 156 ), 157 expCompiledModule: &compiledModule{ 158 executables: &executables{executable: []byte{1, 2, 3, 4, 5}}, 159 functionOffsets: []int{0}, 160 }, 161 expStaleCache: false, 162 expErr: "", 163 }, 164 { 165 name: "two functions", 166 in: concat( 167 magic, 168 []byte{byte(len(testVersion))}, 169 []byte(testVersion), 170 u32.LeBytes(2), // number of functions. 171 // Function index = 0. 172 u64.LeBytes(0), // offset. 173 // Function index = 1. 174 u64.LeBytes(7), // offset. 175 // Executable. 176 u64.LeBytes(10), // size. 177 []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, // machine code. 178 crcf([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}), // crc for machine code. 179 []byte{0}, // no source map. 180 ), 181 importedFunctionCount: 1, 182 expCompiledModule: &compiledModule{ 183 executables: &executables{executable: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, 184 functionOffsets: []int{0, 7}, 185 }, 186 expStaleCache: false, 187 expErr: "", 188 }, 189 { 190 name: "reading executable offset", 191 in: concat( 192 []byte(magic), 193 []byte{byte(len(testVersion))}, 194 []byte(testVersion), 195 u32.LeBytes(2), // number of functions. 196 // Function index = 0. 197 u64.LeBytes(5), // offset. 198 // Function index = 1. 199 ), 200 expErr: "compilationcache: error reading func[1] executable offset: EOF", 201 }, 202 { 203 name: "mmapping", 204 in: concat( 205 []byte(magic), 206 []byte{byte(len(testVersion))}, 207 []byte(testVersion), 208 u32.LeBytes(2), // number of functions. 209 // Function index = 0. 210 u64.LeBytes(0), // offset. 211 // Function index = 1. 212 u64.LeBytes(5), // offset. 213 // Executable. 214 u64.LeBytes(5), // size of the executable. 215 // Lack of machine code here. 216 ), 217 expErr: "compilationcache: error reading executable (len=5): EOF", 218 }, 219 { 220 name: "bad crc", 221 in: concat( 222 magic, 223 []byte{byte(len(testVersion))}, 224 []byte(testVersion), 225 u32.LeBytes(1), // number of functions. 226 u64.LeBytes(0), // offset. 227 // Executable. 228 u64.LeBytes(5), // size. 229 []byte{1, 2, 3, 4, 5}, // machine code. 230 []byte{1, 2, 3, 4}, // crc for machine code. 231 ), 232 expCompiledModule: &compiledModule{ 233 executables: &executables{executable: []byte{1, 2, 3, 4, 5}}, 234 functionOffsets: []int{0}, 235 }, 236 expStaleCache: false, 237 expErr: "compilationcache: checksum mismatch (expected 1397854123, got 67305985)", 238 }, 239 { 240 name: "missing crc", 241 in: concat( 242 magic, 243 []byte{byte(len(testVersion))}, 244 []byte(testVersion), 245 u32.LeBytes(1), // number of functions. 246 u64.LeBytes(0), // offset. 247 // Executable. 248 u64.LeBytes(5), // size. 249 []byte{1, 2, 3, 4, 5}, // machine code. 250 ), 251 expCompiledModule: &compiledModule{ 252 executables: &executables{executable: []byte{1, 2, 3, 4, 5}}, 253 functionOffsets: []int{0}, 254 }, 255 expStaleCache: false, 256 expErr: "compilationcache: could not read checksum: EOF", 257 }, 258 { 259 name: "no source map presence", 260 in: concat( 261 magic, 262 []byte{byte(len(testVersion))}, 263 []byte(testVersion), 264 u32.LeBytes(1), // number of functions. 265 u64.LeBytes(0), // offset. 266 // Executable. 267 u64.LeBytes(5), // size. 268 []byte{1, 2, 3, 4, 5}, // machine code. 269 crcf([]byte{1, 2, 3, 4, 5}), // crc for machine code. 270 ), 271 expCompiledModule: &compiledModule{ 272 executables: &executables{executable: []byte{1, 2, 3, 4, 5}}, 273 functionOffsets: []int{0}, 274 }, 275 expStaleCache: false, 276 expErr: "compilationcache: error reading source map presence: EOF", 277 }, 278 } 279 280 for _, tc := range tests { 281 tc := tc 282 t.Run(tc.name, func(t *testing.T) { 283 cm, staleCache, err := deserializeCompiledModule(testVersion, io.NopCloser(bytes.NewReader(tc.in))) 284 285 if tc.expErr != "" { 286 require.EqualError(t, err, tc.expErr) 287 } else { 288 require.NoError(t, err) 289 require.Equal(t, tc.expCompiledModule, cm) 290 } 291 292 require.Equal(t, tc.expStaleCache, staleCache) 293 }) 294 } 295 } 296 297 func Test_fileCacheKey(t *testing.T) { 298 s := sha256.New() 299 s.Write([]byte("hello world")) 300 m := &wasm.Module{} 301 s.Sum(m.ID[:0]) 302 original := m.ID 303 result := fileCacheKey(m) 304 require.Equal(t, original, m.ID) 305 require.NotEqual(t, original, result) 306 }