github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/engine/interpreter/interpreter_test.go (about) 1 package interpreter 2 3 import ( 4 "context" 5 "fmt" 6 "math" 7 "strconv" 8 "testing" 9 10 "github.com/wasilibs/wazerox/api" 11 "github.com/wasilibs/wazerox/internal/testing/require" 12 "github.com/wasilibs/wazerox/internal/wasm" 13 "github.com/wasilibs/wazerox/internal/wazeroir" 14 ) 15 16 // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors. 17 var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary") 18 19 func TestInterpreter_peekValues(t *testing.T) { 20 ce := &callEngine{} 21 require.Nil(t, ce.peekValues(0)) 22 23 ce.stack = []uint64{5, 4, 3, 2, 1} 24 require.Nil(t, ce.peekValues(0)) 25 require.Equal(t, []uint64{2, 1}, ce.peekValues(2)) 26 } 27 28 func TestInterpreter_CallEngine_PushFrame(t *testing.T) { 29 f1 := &callFrame{} 30 f2 := &callFrame{} 31 32 ce := callEngine{} 33 require.Zero(t, len(ce.frames), "expected no frames") 34 35 ce.pushFrame(f1) 36 require.Equal(t, []*callFrame{f1}, ce.frames) 37 38 ce.pushFrame(f2) 39 require.Equal(t, []*callFrame{f1, f2}, ce.frames) 40 } 41 42 func TestInterpreter_CallEngine_PushFrame_StackOverflow(t *testing.T) { 43 saved := callStackCeiling 44 defer func() { callStackCeiling = saved }() 45 46 callStackCeiling = 3 47 48 f1 := &callFrame{} 49 f2 := &callFrame{} 50 f3 := &callFrame{} 51 f4 := &callFrame{} 52 53 vm := callEngine{} 54 vm.pushFrame(f1) 55 vm.pushFrame(f2) 56 vm.pushFrame(f3) 57 58 captured := require.CapturePanic(func() { vm.pushFrame(f4) }) 59 require.EqualError(t, captured, "stack overflow") 60 } 61 62 func TestInterpreter_NonTrappingFloatToIntConversion(t *testing.T) { 63 _0x80000000 := uint32(0x80000000) 64 _0xffffffff := uint32(0xffffffff) 65 _0x8000000000000000 := uint64(0x8000000000000000) 66 _0xffffffffffffffff := uint64(0xffffffffffffffff) 67 68 tests := []struct { 69 op wasm.OpcodeMisc 70 inputType wazeroir.Float 71 outputType wazeroir.SignedInt 72 input32bit []float32 73 input64bit []float64 74 expected32bit []int32 75 expected64bit []int64 76 }{ 77 { 78 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L261-L282 79 op: wasm.OpcodeMiscI32TruncSatF32S, 80 inputType: wazeroir.Float32, 81 outputType: wazeroir.SignedInt32, 82 input32bit: []float32{ 83 0.0, 0.0, 0x1p-149, -0x1p-149, 1.0, 0x1.19999ap+0, 1.5, -1.0, -0x1.19999ap+0, 84 -1.5, -1.9, -2.0, 2147483520.0, -2147483648.0, 2147483648.0, -2147483904.0, 85 float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN()), float32(math.NaN()), 86 float32(math.NaN()), float32(math.NaN()), 87 }, 88 expected32bit: []int32{ 89 0, 0, 0, 0, 1, 1, 1, -1, -1, -1, -1, -2, 2147483520, -2147483648, 0x7fffffff, 90 int32(_0x80000000), 0x7fffffff, int32(_0x80000000), 0, 0, 0, 0, 91 }, 92 }, 93 { 94 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L284-L304 95 op: wasm.OpcodeMiscI32TruncSatF32U, 96 inputType: wazeroir.Float32, 97 outputType: wazeroir.SignedUint32, 98 input32bit: []float32{ 99 0.0, 0.0, 0x1p-149, -0x1p-149, 1.0, 0x1.19999ap+0, 1.5, 1.9, 2.0, 2147483648, 4294967040.0, 100 -0x1.ccccccp-1, -0x1.fffffep-1, 4294967296.0, -1.0, float32(math.Inf(1)), float32(math.Inf(-1)), 101 float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), 102 }, 103 expected32bit: []int32{ 104 0, 0, 0, 0, 1, 1, 1, 1, 2, -2147483648, -256, 0, 0, int32(_0xffffffff), 0x00000000, 105 int32(_0xffffffff), 0x00000000, 0, 0, 0, 0, 106 }, 107 }, 108 { 109 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L355-L378 110 op: wasm.OpcodeMiscI64TruncSatF32S, 111 inputType: wazeroir.Float32, 112 outputType: wazeroir.SignedInt64, 113 input32bit: []float32{ 114 0.0, 0.0, 0x1p-149, -0x1p-149, 1.0, 0x1.19999ap+0, 1.5, -1.0, -0x1.19999ap+0, -1.5, -1.9, -2.0, 4294967296, 115 -4294967296, 9223371487098961920.0, -9223372036854775808.0, 9223372036854775808.0, -9223373136366403584.0, 116 float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), 117 float32(math.NaN()), 118 }, 119 expected64bit: []int64{ 120 0, 0, 0, 0, 1, 1, 1, -1, -1, -1, -1, -2, 4294967296, -4294967296, 9223371487098961920, -9223372036854775808, 121 0x7fffffffffffffff, int64(_0x8000000000000000), 0x7fffffffffffffff, int64(_0x8000000000000000), 0, 0, 0, 0, 122 }, 123 }, 124 { 125 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L380-L398 126 op: wasm.OpcodeMiscI64TruncSatF32U, 127 inputType: wazeroir.Float32, 128 outputType: wazeroir.SignedUint64, 129 input32bit: []float32{ 130 0.0, 0.0, 0x1p-149, -0x1p-149, 1.0, 0x1.19999ap+0, 1.5, 4294967296, 131 18446742974197923840.0, -0x1.ccccccp-1, -0x1.fffffep-1, 18446744073709551616.0, -1.0, 132 float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN()), float32(math.NaN()), 133 float32(math.NaN()), float32(math.NaN()), 134 }, 135 expected64bit: []int64{ 136 0, 0, 0, 0, 1, 1, 1, 137 4294967296, -1099511627776, 0, 0, int64(_0xffffffffffffffff), 0x0000000000000000, 138 int64(_0xffffffffffffffff), 0x0000000000000000, 0, 0, 0, 0, 139 }, 140 }, 141 { 142 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L306-L327 143 op: wasm.OpcodeMiscI32TruncSatF64S, 144 inputType: wazeroir.Float64, 145 outputType: wazeroir.SignedInt32, 146 input64bit: []float64{ 147 0.0, 0.0, 0x0.0000000000001p-1022, -0x0.0000000000001p-1022, 1.0, 0x1.199999999999ap+0, 1.5, -1.0, 148 -0x1.199999999999ap+0, -1.5, -1.9, -2.0, 2147483647.0, -2147483648.0, 2147483648.0, 149 -2147483649.0, math.Inf(1), math.Inf(-1), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 150 }, 151 expected32bit: []int32{ 152 0, 0, 0, 0, 1, 1, 1, -1, -1, -1, -1, -2, 153 2147483647, -2147483648, 0x7fffffff, int32(_0x80000000), 0x7fffffff, int32(_0x80000000), 0, 154 0, 0, 0, 155 }, 156 }, 157 { 158 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L329-L353 159 op: wasm.OpcodeMiscI32TruncSatF64U, 160 inputType: wazeroir.Float64, 161 outputType: wazeroir.SignedUint32, 162 input64bit: []float64{ 163 0.0, 0.0, 0x0.0000000000001p-1022, -0x0.0000000000001p-1022, 1.0, 0x1.199999999999ap+0, 1.5, 1.9, 2.0, 164 2147483648, 4294967295.0, -0x1.ccccccccccccdp-1, -0x1.fffffffffffffp-1, 1e8, 4294967296.0, -1.0, 1e16, 1e30, 165 9223372036854775808, math.Inf(1), math.Inf(-1), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 166 }, 167 expected32bit: []int32{ 168 0, 0, 0, 0, 1, 1, 1, 1, 2, -2147483648, -1, 169 0, 0, 100000000, int32(_0xffffffff), 0x00000000, int32(_0xffffffff), int32(_0xffffffff), int32(_0xffffffff), 170 int32(_0xffffffff), 0x00000000, 0, 0, 0, 0, 171 }, 172 }, 173 { 174 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L400-L423 175 op: wasm.OpcodeMiscI64TruncSatF64S, 176 inputType: wazeroir.Float64, 177 outputType: wazeroir.SignedInt64, 178 input64bit: []float64{ 179 0.0, 0.0, 0x0.0000000000001p-1022, -0x0.0000000000001p-1022, 1.0, 0x1.199999999999ap+0, 1.5, -1.0, 180 -0x1.199999999999ap+0, -1.5, -1.9, -2.0, 4294967296, -4294967296, 9223372036854774784.0, -9223372036854775808.0, 181 9223372036854775808.0, -9223372036854777856.0, math.Inf(1), math.Inf(-1), math.NaN(), math.NaN(), math.NaN(), 182 math.NaN(), 183 }, 184 expected64bit: []int64{ 185 0, 0, 0, 0, 1, 1, 1, -1, -1, -1, -1, -2, 186 4294967296, -4294967296, 9223372036854774784, -9223372036854775808, 0x7fffffffffffffff, 187 int64(_0x8000000000000000), 0x7fffffffffffffff, int64(_0x8000000000000000), 0, 0, 0, 0, 188 }, 189 }, 190 { 191 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L425-L447 192 op: wasm.OpcodeMiscI64TruncSatF64U, 193 inputType: wazeroir.Float64, 194 outputType: wazeroir.SignedUint64, 195 input64bit: []float64{ 196 0.0, 0.0, 0x0.0000000000001p-1022, -0x0.0000000000001p-1022, 1.0, 0x1.199999999999ap+0, 1.5, 4294967295, 4294967296, 197 18446744073709549568.0, -0x1.ccccccccccccdp-1, -0x1.fffffffffffffp-1, 1e8, 1e16, 9223372036854775808, 198 18446744073709551616.0, -1.0, math.Inf(1), math.Inf(-1), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 199 }, 200 expected64bit: []int64{ 201 0, 0, 0, 0, 1, 1, 1, 0xffffffff, 0x100000000, -2048, 0, 0, 100000000, 10000000000000000, 202 -9223372036854775808, int64(_0xffffffffffffffff), 0x0000000000000000, int64(_0xffffffffffffffff), 203 0x0000000000000000, 0, 0, 0, 0, 204 }, 205 }, 206 } 207 208 for _, tt := range tests { 209 tc := tt 210 t.Run(wasm.MiscInstructionName(tc.op), func(t *testing.T) { 211 in32bit := len(tc.input32bit) > 0 212 casenum := len(tc.input32bit) 213 if !in32bit { 214 casenum = len(tc.input64bit) 215 } 216 for i := 0; i < casenum; i++ { 217 i := i 218 t.Run(strconv.Itoa(i), func(t *testing.T) { 219 var body []wazeroir.UnionOperation 220 if in32bit { 221 body = append(body, wazeroir.UnionOperation{ 222 Kind: wazeroir.OperationKindConstF32, 223 U1: uint64(math.Float32bits(tc.input32bit[i])), 224 }) 225 } else { 226 body = append(body, wazeroir.UnionOperation{ 227 Kind: wazeroir.OperationKindConstF64, 228 U1: uint64(math.Float64bits(tc.input64bit[i])), 229 }) 230 } 231 232 body = append(body, wazeroir.UnionOperation{ 233 Kind: wazeroir.OperationKindITruncFromF, 234 B1: byte(tc.inputType), 235 B2: byte(tc.outputType), 236 B3: true, // NonTrapping = true. 237 }) 238 239 // Return from function. 240 body = append(body, 241 wazeroir.UnionOperation{Kind: wazeroir.OperationKindBr, U1: uint64(math.MaxUint64)}, 242 ) 243 244 ce := &callEngine{} 245 f := &function{ 246 moduleInstance: &wasm.ModuleInstance{Engine: &moduleEngine{}}, 247 parent: &compiledFunction{body: body}, 248 } 249 ce.callNativeFunc(testCtx, &wasm.ModuleInstance{}, f) 250 251 if len(tc.expected32bit) > 0 { 252 require.Equal(t, tc.expected32bit[i], int32(uint32(ce.popValue()))) 253 } else { 254 require.Equal(t, tc.expected64bit[i], int64((ce.popValue()))) 255 } 256 }) 257 } 258 }) 259 260 } 261 } 262 263 func TestInterpreter_CallEngine_callNativeFunc_signExtend(t *testing.T) { 264 translateToIROperationKind := func(op wasm.Opcode) (kind wazeroir.OperationKind) { 265 switch op { 266 case wasm.OpcodeI32Extend8S: 267 kind = wazeroir.OperationKindSignExtend32From8 268 case wasm.OpcodeI32Extend16S: 269 kind = wazeroir.OperationKindSignExtend32From16 270 case wasm.OpcodeI64Extend8S: 271 kind = wazeroir.OperationKindSignExtend64From8 272 case wasm.OpcodeI64Extend16S: 273 kind = wazeroir.OperationKindSignExtend64From16 274 case wasm.OpcodeI64Extend32S: 275 kind = wazeroir.OperationKindSignExtend64From32 276 } 277 return 278 } 279 t.Run("32bit", func(t *testing.T) { 280 tests := []struct { 281 in int32 282 expected int32 283 opcode wasm.Opcode 284 }{ 285 // https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i32.wast#L270-L276 286 {in: 0, expected: 0, opcode: wasm.OpcodeI32Extend8S}, 287 {in: 0x7f, expected: 127, opcode: wasm.OpcodeI32Extend8S}, 288 {in: 0x80, expected: -128, opcode: wasm.OpcodeI32Extend8S}, 289 {in: 0xff, expected: -1, opcode: wasm.OpcodeI32Extend8S}, 290 {in: 0x012345_00, expected: 0, opcode: wasm.OpcodeI32Extend8S}, 291 {in: -19088768 /* = 0xfedcba_80 bit pattern */, expected: -0x80, opcode: wasm.OpcodeI32Extend8S}, 292 {in: -1, expected: -1, opcode: wasm.OpcodeI32Extend8S}, 293 294 // https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i32.wast#L278-L284 295 {in: 0, expected: 0, opcode: wasm.OpcodeI32Extend16S}, 296 {in: 0x7fff, expected: 32767, opcode: wasm.OpcodeI32Extend16S}, 297 {in: 0x8000, expected: -32768, opcode: wasm.OpcodeI32Extend16S}, 298 {in: 0xffff, expected: -1, opcode: wasm.OpcodeI32Extend16S}, 299 {in: 0x0123_0000, expected: 0, opcode: wasm.OpcodeI32Extend16S}, 300 {in: -19103744 /* = 0xfedc_8000 bit pattern */, expected: -0x8000, opcode: wasm.OpcodeI32Extend16S}, 301 {in: -1, expected: -1, opcode: wasm.OpcodeI32Extend16S}, 302 } 303 304 for _, tt := range tests { 305 tc := tt 306 t.Run(fmt.Sprintf("%s(i32.const(0x%x))", wasm.InstructionName(tc.opcode), tc.in), func(t *testing.T) { 307 ce := &callEngine{} 308 f := &function{ 309 moduleInstance: &wasm.ModuleInstance{Engine: &moduleEngine{}}, 310 parent: &compiledFunction{body: []wazeroir.UnionOperation{ 311 {Kind: wazeroir.OperationKindConstI32, U1: uint64(uint32(tc.in))}, 312 {Kind: translateToIROperationKind(tc.opcode)}, 313 {Kind: wazeroir.OperationKindBr, U1: uint64(math.MaxUint64)}, 314 }}, 315 } 316 ce.callNativeFunc(testCtx, &wasm.ModuleInstance{}, f) 317 require.Equal(t, tc.expected, int32(uint32(ce.popValue()))) 318 }) 319 } 320 }) 321 t.Run("64bit", func(t *testing.T) { 322 tests := []struct { 323 in int64 324 expected int64 325 opcode wasm.Opcode 326 }{ 327 // https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i64.wast#L271-L277 328 {in: 0, expected: 0, opcode: wasm.OpcodeI64Extend8S}, 329 {in: 0x7f, expected: 127, opcode: wasm.OpcodeI64Extend8S}, 330 {in: 0x80, expected: -128, opcode: wasm.OpcodeI64Extend8S}, 331 {in: 0xff, expected: -1, opcode: wasm.OpcodeI64Extend8S}, 332 {in: 0x01234567_89abcd_00, expected: 0, opcode: wasm.OpcodeI64Extend8S}, 333 {in: 81985529216486784 /* = 0xfedcba98_765432_80 bit pattern */, expected: -0x80, opcode: wasm.OpcodeI64Extend8S}, 334 {in: -1, expected: -1, opcode: wasm.OpcodeI64Extend8S}, 335 336 // https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i64.wast#L279-L285 337 {in: 0, expected: 0, opcode: wasm.OpcodeI64Extend16S}, 338 {in: 0x7fff, expected: 32767, opcode: wasm.OpcodeI64Extend16S}, 339 {in: 0x8000, expected: -32768, opcode: wasm.OpcodeI64Extend16S}, 340 {in: 0xffff, expected: -1, opcode: wasm.OpcodeI64Extend16S}, 341 {in: 0x12345678_9abc_0000, expected: 0, opcode: wasm.OpcodeI64Extend16S}, 342 {in: 81985529216466944 /* = 0xfedcba98_7654_8000 bit pattern */, expected: -0x8000, opcode: wasm.OpcodeI64Extend16S}, 343 {in: -1, expected: -1, opcode: wasm.OpcodeI64Extend16S}, 344 345 // https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i64.wast#L287-L296 346 {in: 0, expected: 0, opcode: wasm.OpcodeI64Extend32S}, 347 {in: 0x7fff, expected: 32767, opcode: wasm.OpcodeI64Extend32S}, 348 {in: 0x8000, expected: 32768, opcode: wasm.OpcodeI64Extend32S}, 349 {in: 0xffff, expected: 65535, opcode: wasm.OpcodeI64Extend32S}, 350 {in: 0x7fffffff, expected: 0x7fffffff, opcode: wasm.OpcodeI64Extend32S}, 351 {in: 0x80000000, expected: -0x80000000, opcode: wasm.OpcodeI64Extend32S}, 352 {in: 0xffffffff, expected: -1, opcode: wasm.OpcodeI64Extend32S}, 353 {in: 0x01234567_00000000, expected: 0, opcode: wasm.OpcodeI64Extend32S}, 354 {in: -81985529054232576 /* = 0xfedcba98_80000000 bit pattern */, expected: -0x80000000, opcode: wasm.OpcodeI64Extend32S}, 355 {in: -1, expected: -1, opcode: wasm.OpcodeI64Extend32S}, 356 } 357 358 for _, tt := range tests { 359 tc := tt 360 t.Run(fmt.Sprintf("%s(i64.const(0x%x))", wasm.InstructionName(tc.opcode), tc.in), func(t *testing.T) { 361 ce := &callEngine{} 362 f := &function{ 363 moduleInstance: &wasm.ModuleInstance{Engine: &moduleEngine{}}, 364 parent: &compiledFunction{body: []wazeroir.UnionOperation{ 365 {Kind: wazeroir.OperationKindConstI64, U1: uint64(tc.in)}, 366 {Kind: translateToIROperationKind(tc.opcode)}, 367 {Kind: wazeroir.OperationKindBr, U1: uint64(math.MaxUint64)}, 368 }}, 369 } 370 ce.callNativeFunc(testCtx, &wasm.ModuleInstance{}, f) 371 require.Equal(t, tc.expected, int64(ce.popValue())) 372 }) 373 } 374 }) 375 } 376 377 func TestInterpreter_Compile(t *testing.T) { 378 t.Run("uncompiled", func(t *testing.T) { 379 e := NewEngine(testCtx, api.CoreFeaturesV1, nil).(*engine) 380 _, err := e.NewModuleEngine( 381 &wasm.Module{}, 382 nil, // functions 383 ) 384 require.EqualError(t, err, "source module must be compiled before instantiation") 385 }) 386 t.Run("fail", func(t *testing.T) { 387 e := NewEngine(testCtx, api.CoreFeaturesV1, nil).(*engine) 388 389 errModule := &wasm.Module{ 390 TypeSection: []wasm.FunctionType{{}}, 391 FunctionSection: []wasm.Index{0, 0, 0}, 392 CodeSection: []wasm.Code{ 393 {Body: []byte{wasm.OpcodeEnd}}, 394 {Body: []byte{wasm.OpcodeEnd}}, 395 {Body: []byte{wasm.OpcodeCall}}, // Call instruction without immediate for call target index is invalid and should fail to compile. 396 }, 397 ID: wasm.ModuleID{}, 398 } 399 400 err := e.CompileModule(testCtx, errModule, nil, false) 401 require.EqualError(t, err, "handling instruction: apply stack failed for call: reading immediates: EOF") 402 403 // On the compilation failure, all the compiled functions including succeeded ones must be released. 404 _, ok := e.compiledFunctions[errModule.ID] 405 require.False(t, ok) 406 }) 407 t.Run("ok", func(t *testing.T) { 408 e := NewEngine(testCtx, api.CoreFeaturesV1, nil).(*engine) 409 410 okModule := &wasm.Module{ 411 TypeSection: []wasm.FunctionType{{}}, 412 FunctionSection: []wasm.Index{0, 0, 0, 0}, 413 CodeSection: []wasm.Code{ 414 {Body: []byte{wasm.OpcodeEnd}}, 415 {Body: []byte{wasm.OpcodeEnd}}, 416 {Body: []byte{wasm.OpcodeEnd}}, 417 {Body: []byte{wasm.OpcodeEnd}}, 418 }, 419 ID: wasm.ModuleID{}, 420 } 421 err := e.CompileModule(testCtx, okModule, nil, false) 422 require.NoError(t, err) 423 424 compiled, ok := e.compiledFunctions[okModule.ID] 425 require.True(t, ok) 426 require.Equal(t, len(okModule.FunctionSection), len(compiled)) 427 428 _, ok = e.compiledFunctions[okModule.ID] 429 require.True(t, ok) 430 }) 431 } 432 433 func TestEngine_CachedCompiledFunctionPerModule(t *testing.T) { 434 e := NewEngine(testCtx, api.CoreFeaturesV1, nil).(*engine) 435 exp := []compiledFunction{ 436 {body: []wazeroir.UnionOperation{}}, 437 {body: []wazeroir.UnionOperation{}}, 438 } 439 m := &wasm.Module{} 440 441 e.addCompiledFunctions(m, exp) 442 443 actual, ok := e.getCompiledFunctions(m) 444 require.True(t, ok) 445 require.Equal(t, len(exp), len(actual)) 446 for i := range actual { 447 require.Equal(t, exp[i], actual[i]) 448 } 449 450 e.deleteCompiledFunctions(m) 451 _, ok = e.getCompiledFunctions(m) 452 require.False(t, ok) 453 }