wa-lang.org/wazero@v1.0.2/internal/engine/interpreter/interpreter_test.go (about) 1 package interpreter 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "math" 8 "strconv" 9 "testing" 10 "unsafe" 11 12 "wa-lang.org/wazero/api" 13 "wa-lang.org/wazero/experimental" 14 "wa-lang.org/wazero/experimental/logging" 15 "wa-lang.org/wazero/internal/testing/enginetest" 16 "wa-lang.org/wazero/internal/testing/require" 17 "wa-lang.org/wazero/internal/wasm" 18 "wa-lang.org/wazero/internal/wazeroir" 19 ) 20 21 // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors. 22 var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary") 23 24 func TestInterpreter_peekValues(t *testing.T) { 25 ce := &callEngine{} 26 require.Nil(t, ce.peekValues(0)) 27 28 ce.stack = []uint64{5, 4, 3, 2, 1} 29 require.Nil(t, ce.peekValues(0)) 30 require.Equal(t, []uint64{2, 1}, ce.peekValues(2)) 31 } 32 33 func TestInterpreter_CallEngine_PushFrame(t *testing.T) { 34 f1 := &callFrame{} 35 f2 := &callFrame{} 36 37 ce := callEngine{} 38 require.Zero(t, len(ce.frames), "expected no frames") 39 40 ce.pushFrame(f1) 41 require.Equal(t, []*callFrame{f1}, ce.frames) 42 43 ce.pushFrame(f2) 44 require.Equal(t, []*callFrame{f1, f2}, ce.frames) 45 } 46 47 func TestInterpreter_CallEngine_PushFrame_StackOverflow(t *testing.T) { 48 saved := callStackCeiling 49 defer func() { callStackCeiling = saved }() 50 51 callStackCeiling = 3 52 53 f1 := &callFrame{} 54 f2 := &callFrame{} 55 f3 := &callFrame{} 56 f4 := &callFrame{} 57 58 vm := callEngine{} 59 vm.pushFrame(f1) 60 vm.pushFrame(f2) 61 vm.pushFrame(f3) 62 63 captured := require.CapturePanic(func() { vm.pushFrame(f4) }) 64 require.EqualError(t, captured, "stack overflow") 65 } 66 67 // et is used for tests defined in the enginetest package. 68 var ( 69 et = &engineTester{} 70 functionLog bytes.Buffer 71 listenerFactory = logging.NewLoggingListenerFactory(&functionLog) 72 ) 73 74 // engineTester implements enginetest.EngineTester. 75 type engineTester struct{} 76 77 // IsCompiler implements enginetest.EngineTester NewEngine. 78 func (e engineTester) IsCompiler() bool { 79 return false 80 } 81 82 // ListenerFactory implements enginetest.EngineTester NewEngine. 83 func (e engineTester) ListenerFactory() experimental.FunctionListenerFactory { 84 return listenerFactory 85 } 86 87 // NewEngine implements enginetest.EngineTester NewEngine. 88 func (e engineTester) NewEngine(enabledFeatures api.CoreFeatures) wasm.Engine { 89 return NewEngine(context.Background(), enabledFeatures) 90 } 91 92 // CompiledFunctionPointerValue implements enginetest.EngineTester CompiledFunctionPointerValue. 93 func (e engineTester) CompiledFunctionPointerValue(me wasm.ModuleEngine, funcIndex wasm.Index) uint64 { 94 internal := me.(*moduleEngine) 95 return uint64(uintptr(unsafe.Pointer(internal.functions[funcIndex]))) 96 } 97 98 func TestInterpreter_Engine_NewModuleEngine(t *testing.T) { 99 enginetest.RunTestEngine_NewModuleEngine(t, et) 100 } 101 102 func TestInterpreter_Engine_InitializeFuncrefGlobals(t *testing.T) { 103 enginetest.RunTestEngine_InitializeFuncrefGlobals(t, et) 104 } 105 106 func TestInterpreter_ModuleEngine_LookupFunction(t *testing.T) { 107 enginetest.RunTestModuleEngine_LookupFunction(t, et) 108 } 109 110 func TestInterpreter_ModuleEngine_Call(t *testing.T) { 111 defer functionLog.Reset() 112 enginetest.RunTestModuleEngine_Call(t, et) 113 require.Equal(t, ` 114 --> .$0(1,2) 115 <-- (1,2) 116 `, "\n"+functionLog.String()) 117 } 118 119 func TestInterpreter_ModuleEngine_Call_HostFn(t *testing.T) { 120 defer functionLog.Reset() 121 enginetest.RunTestModuleEngine_Call_HostFn(t, et) 122 } 123 124 func TestInterpreter_ModuleEngine_Call_Errors(t *testing.T) { 125 defer functionLog.Reset() 126 enginetest.RunTestModuleEngine_Call_Errors(t, et) 127 128 // TODO: Currently, the listener doesn't get notified on errors as they are 129 // implemented with panic. This means the end hooks aren't make resulting 130 // in dangling logs like this: 131 // ==> host.host_div_by(4294967295) 132 // instead of seeing a return like 133 // <== DivByZero 134 require.Equal(t, ` 135 --> imported.div_by.wasm(1) 136 <-- (1) 137 --> imported.div_by.wasm(1) 138 <-- (1) 139 --> imported.div_by.wasm(0) 140 --> imported.div_by.wasm(1) 141 <-- (1) 142 --> imported.call->div_by.go(4294967295) 143 ==> host.div_by.go(4294967295) 144 --> imported.call->div_by.go(1) 145 ==> host.div_by.go(1) 146 <== (1) 147 <-- (1) 148 --> importing.call_import->call->div_by.go(0) 149 --> imported.call->div_by.go(0) 150 ==> host.div_by.go(0) 151 --> importing.call_import->call->div_by.go(1) 152 --> imported.call->div_by.go(1) 153 ==> host.div_by.go(1) 154 <== (1) 155 <-- (1) 156 <-- (1) 157 --> importing.call_import->call->div_by.go(4294967295) 158 --> imported.call->div_by.go(4294967295) 159 ==> host.div_by.go(4294967295) 160 --> importing.call_import->call->div_by.go(1) 161 --> imported.call->div_by.go(1) 162 ==> host.div_by.go(1) 163 <== (1) 164 <-- (1) 165 <-- (1) 166 --> importing.call_import->call->div_by.go(0) 167 --> imported.call->div_by.go(0) 168 ==> host.div_by.go(0) 169 --> importing.call_import->call->div_by.go(1) 170 --> imported.call->div_by.go(1) 171 ==> host.div_by.go(1) 172 <== (1) 173 <-- (1) 174 <-- (1) 175 `, "\n"+functionLog.String()) 176 } 177 178 func TestInterpreter_ModuleEngine_Memory(t *testing.T) { 179 enginetest.RunTestModuleEngine_Memory(t, et) 180 } 181 182 func TestInterpreter_NonTrappingFloatToIntConversion(t *testing.T) { 183 _0x80000000 := uint32(0x80000000) 184 _0xffffffff := uint32(0xffffffff) 185 _0x8000000000000000 := uint64(0x8000000000000000) 186 _0xffffffffffffffff := uint64(0xffffffffffffffff) 187 188 tests := []struct { 189 op wasm.OpcodeMisc 190 inputType wazeroir.Float 191 outputType wazeroir.SignedInt 192 input32bit []float32 193 input64bit []float64 194 expected32bit []int32 195 expected64bit []int64 196 }{ 197 { 198 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L261-L282 199 op: wasm.OpcodeMiscI32TruncSatF32S, 200 inputType: wazeroir.Float32, 201 outputType: wazeroir.SignedInt32, 202 input32bit: []float32{ 203 0.0, 0.0, 0x1p-149, -0x1p-149, 1.0, 0x1.19999ap+0, 1.5, -1.0, -0x1.19999ap+0, 204 -1.5, -1.9, -2.0, 2147483520.0, -2147483648.0, 2147483648.0, -2147483904.0, 205 float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN()), float32(math.NaN()), 206 float32(math.NaN()), float32(math.NaN()), 207 }, 208 expected32bit: []int32{ 209 0, 0, 0, 0, 1, 1, 1, -1, -1, -1, -1, -2, 2147483520, -2147483648, 0x7fffffff, 210 int32(_0x80000000), 0x7fffffff, int32(_0x80000000), 0, 0, 0, 0, 211 }, 212 }, 213 { 214 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L284-L304 215 op: wasm.OpcodeMiscI32TruncSatF32U, 216 inputType: wazeroir.Float32, 217 outputType: wazeroir.SignedUint32, 218 input32bit: []float32{ 219 0.0, 0.0, 0x1p-149, -0x1p-149, 1.0, 0x1.19999ap+0, 1.5, 1.9, 2.0, 2147483648, 4294967040.0, 220 -0x1.ccccccp-1, -0x1.fffffep-1, 4294967296.0, -1.0, float32(math.Inf(1)), float32(math.Inf(-1)), 221 float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), 222 }, 223 expected32bit: []int32{ 224 0, 0, 0, 0, 1, 1, 1, 1, 2, -2147483648, -256, 0, 0, int32(_0xffffffff), 0x00000000, 225 int32(_0xffffffff), 0x00000000, 0, 0, 0, 0, 226 }, 227 }, 228 { 229 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L355-L378 230 op: wasm.OpcodeMiscI64TruncSatF32S, 231 inputType: wazeroir.Float32, 232 outputType: wazeroir.SignedInt64, 233 input32bit: []float32{ 234 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, 235 -4294967296, 9223371487098961920.0, -9223372036854775808.0, 9223372036854775808.0, -9223373136366403584.0, 236 float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), 237 float32(math.NaN()), 238 }, 239 expected64bit: []int64{ 240 0, 0, 0, 0, 1, 1, 1, -1, -1, -1, -1, -2, 4294967296, -4294967296, 9223371487098961920, -9223372036854775808, 241 0x7fffffffffffffff, int64(_0x8000000000000000), 0x7fffffffffffffff, int64(_0x8000000000000000), 0, 0, 0, 0, 242 }, 243 }, 244 { 245 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L380-L398 246 op: wasm.OpcodeMiscI64TruncSatF32U, 247 inputType: wazeroir.Float32, 248 outputType: wazeroir.SignedUint64, 249 input32bit: []float32{ 250 0.0, 0.0, 0x1p-149, -0x1p-149, 1.0, 0x1.19999ap+0, 1.5, 4294967296, 251 18446742974197923840.0, -0x1.ccccccp-1, -0x1.fffffep-1, 18446744073709551616.0, -1.0, 252 float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN()), float32(math.NaN()), 253 float32(math.NaN()), float32(math.NaN()), 254 }, 255 expected64bit: []int64{ 256 0, 0, 0, 0, 1, 1, 1, 257 4294967296, -1099511627776, 0, 0, int64(_0xffffffffffffffff), 0x0000000000000000, 258 int64(_0xffffffffffffffff), 0x0000000000000000, 0, 0, 0, 0, 259 }, 260 }, 261 { 262 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L306-L327 263 op: wasm.OpcodeMiscI32TruncSatF64S, 264 inputType: wazeroir.Float64, 265 outputType: wazeroir.SignedInt32, 266 input64bit: []float64{ 267 0.0, 0.0, 0x0.0000000000001p-1022, -0x0.0000000000001p-1022, 1.0, 0x1.199999999999ap+0, 1.5, -1.0, 268 -0x1.199999999999ap+0, -1.5, -1.9, -2.0, 2147483647.0, -2147483648.0, 2147483648.0, 269 -2147483649.0, math.Inf(1), math.Inf(-1), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 270 }, 271 expected32bit: []int32{ 272 0, 0, 0, 0, 1, 1, 1, -1, -1, -1, -1, -2, 273 2147483647, -2147483648, 0x7fffffff, int32(_0x80000000), 0x7fffffff, int32(_0x80000000), 0, 274 0, 0, 0, 275 }, 276 }, 277 { 278 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L329-L353 279 op: wasm.OpcodeMiscI32TruncSatF64U, 280 inputType: wazeroir.Float64, 281 outputType: wazeroir.SignedUint32, 282 input64bit: []float64{ 283 0.0, 0.0, 0x0.0000000000001p-1022, -0x0.0000000000001p-1022, 1.0, 0x1.199999999999ap+0, 1.5, 1.9, 2.0, 284 2147483648, 4294967295.0, -0x1.ccccccccccccdp-1, -0x1.fffffffffffffp-1, 1e8, 4294967296.0, -1.0, 1e16, 1e30, 285 9223372036854775808, math.Inf(1), math.Inf(-1), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 286 }, 287 expected32bit: []int32{ 288 0, 0, 0, 0, 1, 1, 1, 1, 2, -2147483648, -1, 289 0, 0, 100000000, int32(_0xffffffff), 0x00000000, int32(_0xffffffff), int32(_0xffffffff), int32(_0xffffffff), 290 int32(_0xffffffff), 0x00000000, 0, 0, 0, 0, 291 }, 292 }, 293 { 294 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L400-L423 295 op: wasm.OpcodeMiscI64TruncSatF64S, 296 inputType: wazeroir.Float64, 297 outputType: wazeroir.SignedInt64, 298 input64bit: []float64{ 299 0.0, 0.0, 0x0.0000000000001p-1022, -0x0.0000000000001p-1022, 1.0, 0x1.199999999999ap+0, 1.5, -1.0, 300 -0x1.199999999999ap+0, -1.5, -1.9, -2.0, 4294967296, -4294967296, 9223372036854774784.0, -9223372036854775808.0, 301 9223372036854775808.0, -9223372036854777856.0, math.Inf(1), math.Inf(-1), math.NaN(), math.NaN(), math.NaN(), 302 math.NaN(), 303 }, 304 expected64bit: []int64{ 305 0, 0, 0, 0, 1, 1, 1, -1, -1, -1, -1, -2, 306 4294967296, -4294967296, 9223372036854774784, -9223372036854775808, 0x7fffffffffffffff, 307 int64(_0x8000000000000000), 0x7fffffffffffffff, int64(_0x8000000000000000), 0, 0, 0, 0, 308 }, 309 }, 310 { 311 // https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L425-L447 312 op: wasm.OpcodeMiscI64TruncSatF64U, 313 inputType: wazeroir.Float64, 314 outputType: wazeroir.SignedUint64, 315 input64bit: []float64{ 316 0.0, 0.0, 0x0.0000000000001p-1022, -0x0.0000000000001p-1022, 1.0, 0x1.199999999999ap+0, 1.5, 4294967295, 4294967296, 317 18446744073709549568.0, -0x1.ccccccccccccdp-1, -0x1.fffffffffffffp-1, 1e8, 1e16, 9223372036854775808, 318 18446744073709551616.0, -1.0, math.Inf(1), math.Inf(-1), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 319 }, 320 expected64bit: []int64{ 321 0, 0, 0, 0, 1, 1, 1, 0xffffffff, 0x100000000, -2048, 0, 0, 100000000, 10000000000000000, 322 -9223372036854775808, int64(_0xffffffffffffffff), 0x0000000000000000, int64(_0xffffffffffffffff), 323 0x0000000000000000, 0, 0, 0, 0, 324 }, 325 }, 326 } 327 328 for _, tt := range tests { 329 tc := tt 330 t.Run(wasm.MiscInstructionName(tc.op), func(t *testing.T) { 331 in32bit := len(tc.input32bit) > 0 332 casenum := len(tc.input32bit) 333 if !in32bit { 334 casenum = len(tc.input64bit) 335 } 336 for i := 0; i < casenum; i++ { 337 i := i 338 t.Run(strconv.Itoa(i), func(t *testing.T) { 339 var body []*interpreterOp 340 if in32bit { 341 body = append(body, &interpreterOp{ 342 kind: wazeroir.OperationKindConstF32, 343 us: []uint64{uint64(math.Float32bits(tc.input32bit[i]))}, 344 }) 345 } else { 346 body = append(body, &interpreterOp{ 347 kind: wazeroir.OperationKindConstF64, 348 us: []uint64{uint64(math.Float64bits(tc.input64bit[i]))}, 349 }) 350 } 351 352 body = append(body, &interpreterOp{ 353 kind: wazeroir.OperationKindITruncFromF, 354 b1: byte(tc.inputType), 355 b2: byte(tc.outputType), 356 b3: true, // NonTrapping = true. 357 }) 358 359 // Return from function. 360 body = append(body, 361 &interpreterOp{kind: wazeroir.OperationKindBr, us: []uint64{math.MaxUint64}}, 362 ) 363 364 ce := &callEngine{} 365 f := &function{ 366 source: &wasm.FunctionInstance{Module: &wasm.ModuleInstance{Engine: &moduleEngine{}}}, 367 body: body, 368 } 369 ce.callNativeFunc(testCtx, &wasm.CallContext{}, f) 370 371 if len(tc.expected32bit) > 0 { 372 require.Equal(t, tc.expected32bit[i], int32(uint32(ce.popValue()))) 373 } else { 374 require.Equal(t, tc.expected64bit[i], int64((ce.popValue()))) 375 } 376 }) 377 } 378 }) 379 380 } 381 } 382 383 func TestInterpreter_CallEngine_callNativeFunc_signExtend(t *testing.T) { 384 translateToIROperationKind := func(op wasm.Opcode) (kind wazeroir.OperationKind) { 385 switch op { 386 case wasm.OpcodeI32Extend8S: 387 kind = wazeroir.OperationKindSignExtend32From8 388 case wasm.OpcodeI32Extend16S: 389 kind = wazeroir.OperationKindSignExtend32From16 390 case wasm.OpcodeI64Extend8S: 391 kind = wazeroir.OperationKindSignExtend64From8 392 case wasm.OpcodeI64Extend16S: 393 kind = wazeroir.OperationKindSignExtend64From16 394 case wasm.OpcodeI64Extend32S: 395 kind = wazeroir.OperationKindSignExtend64From32 396 } 397 return 398 } 399 t.Run("32bit", func(t *testing.T) { 400 tests := []struct { 401 in int32 402 expected int32 403 opcode wasm.Opcode 404 }{ 405 // https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i32.wast#L270-L276 406 {in: 0, expected: 0, opcode: wasm.OpcodeI32Extend8S}, 407 {in: 0x7f, expected: 127, opcode: wasm.OpcodeI32Extend8S}, 408 {in: 0x80, expected: -128, opcode: wasm.OpcodeI32Extend8S}, 409 {in: 0xff, expected: -1, opcode: wasm.OpcodeI32Extend8S}, 410 {in: 0x012345_00, expected: 0, opcode: wasm.OpcodeI32Extend8S}, 411 {in: -19088768 /* = 0xfedcba_80 bit pattern */, expected: -0x80, opcode: wasm.OpcodeI32Extend8S}, 412 {in: -1, expected: -1, opcode: wasm.OpcodeI32Extend8S}, 413 414 // https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i32.wast#L278-L284 415 {in: 0, expected: 0, opcode: wasm.OpcodeI32Extend16S}, 416 {in: 0x7fff, expected: 32767, opcode: wasm.OpcodeI32Extend16S}, 417 {in: 0x8000, expected: -32768, opcode: wasm.OpcodeI32Extend16S}, 418 {in: 0xffff, expected: -1, opcode: wasm.OpcodeI32Extend16S}, 419 {in: 0x0123_0000, expected: 0, opcode: wasm.OpcodeI32Extend16S}, 420 {in: -19103744 /* = 0xfedc_8000 bit pattern */, expected: -0x8000, opcode: wasm.OpcodeI32Extend16S}, 421 {in: -1, expected: -1, opcode: wasm.OpcodeI32Extend16S}, 422 } 423 424 for _, tt := range tests { 425 tc := tt 426 t.Run(fmt.Sprintf("%s(i32.const(0x%x))", wasm.InstructionName(tc.opcode), tc.in), func(t *testing.T) { 427 ce := &callEngine{} 428 f := &function{ 429 source: &wasm.FunctionInstance{Module: &wasm.ModuleInstance{Engine: &moduleEngine{}}}, 430 body: []*interpreterOp{ 431 {kind: wazeroir.OperationKindConstI32, us: []uint64{uint64(uint32(tc.in))}}, 432 {kind: translateToIROperationKind(tc.opcode)}, 433 {kind: wazeroir.OperationKindBr, us: []uint64{math.MaxUint64}}, 434 }, 435 } 436 ce.callNativeFunc(testCtx, &wasm.CallContext{}, f) 437 require.Equal(t, tc.expected, int32(uint32(ce.popValue()))) 438 }) 439 } 440 }) 441 t.Run("64bit", func(t *testing.T) { 442 tests := []struct { 443 in int64 444 expected int64 445 opcode wasm.Opcode 446 }{ 447 // https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i64.wast#L271-L277 448 {in: 0, expected: 0, opcode: wasm.OpcodeI64Extend8S}, 449 {in: 0x7f, expected: 127, opcode: wasm.OpcodeI64Extend8S}, 450 {in: 0x80, expected: -128, opcode: wasm.OpcodeI64Extend8S}, 451 {in: 0xff, expected: -1, opcode: wasm.OpcodeI64Extend8S}, 452 {in: 0x01234567_89abcd_00, expected: 0, opcode: wasm.OpcodeI64Extend8S}, 453 {in: 81985529216486784 /* = 0xfedcba98_765432_80 bit pattern */, expected: -0x80, opcode: wasm.OpcodeI64Extend8S}, 454 {in: -1, expected: -1, opcode: wasm.OpcodeI64Extend8S}, 455 456 // https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i64.wast#L279-L285 457 {in: 0, expected: 0, opcode: wasm.OpcodeI64Extend16S}, 458 {in: 0x7fff, expected: 32767, opcode: wasm.OpcodeI64Extend16S}, 459 {in: 0x8000, expected: -32768, opcode: wasm.OpcodeI64Extend16S}, 460 {in: 0xffff, expected: -1, opcode: wasm.OpcodeI64Extend16S}, 461 {in: 0x12345678_9abc_0000, expected: 0, opcode: wasm.OpcodeI64Extend16S}, 462 {in: 81985529216466944 /* = 0xfedcba98_7654_8000 bit pattern */, expected: -0x8000, opcode: wasm.OpcodeI64Extend16S}, 463 {in: -1, expected: -1, opcode: wasm.OpcodeI64Extend16S}, 464 465 // https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i64.wast#L287-L296 466 {in: 0, expected: 0, opcode: wasm.OpcodeI64Extend32S}, 467 {in: 0x7fff, expected: 32767, opcode: wasm.OpcodeI64Extend32S}, 468 {in: 0x8000, expected: 32768, opcode: wasm.OpcodeI64Extend32S}, 469 {in: 0xffff, expected: 65535, opcode: wasm.OpcodeI64Extend32S}, 470 {in: 0x7fffffff, expected: 0x7fffffff, opcode: wasm.OpcodeI64Extend32S}, 471 {in: 0x80000000, expected: -0x80000000, opcode: wasm.OpcodeI64Extend32S}, 472 {in: 0xffffffff, expected: -1, opcode: wasm.OpcodeI64Extend32S}, 473 {in: 0x01234567_00000000, expected: 0, opcode: wasm.OpcodeI64Extend32S}, 474 {in: -81985529054232576 /* = 0xfedcba98_80000000 bit pattern */, expected: -0x80000000, opcode: wasm.OpcodeI64Extend32S}, 475 {in: -1, expected: -1, opcode: wasm.OpcodeI64Extend32S}, 476 } 477 478 for _, tt := range tests { 479 tc := tt 480 t.Run(fmt.Sprintf("%s(i64.const(0x%x))", wasm.InstructionName(tc.opcode), tc.in), func(t *testing.T) { 481 ce := &callEngine{} 482 f := &function{ 483 source: &wasm.FunctionInstance{Module: &wasm.ModuleInstance{Engine: &moduleEngine{}}}, 484 body: []*interpreterOp{ 485 {kind: wazeroir.OperationKindConstI64, us: []uint64{uint64(tc.in)}}, 486 {kind: translateToIROperationKind(tc.opcode)}, 487 {kind: wazeroir.OperationKindBr, us: []uint64{math.MaxUint64}}, 488 }, 489 } 490 ce.callNativeFunc(testCtx, &wasm.CallContext{}, f) 491 require.Equal(t, tc.expected, int64(ce.popValue())) 492 }) 493 } 494 }) 495 } 496 497 func TestInterpreter_Compile(t *testing.T) { 498 t.Run("uncompiled", func(t *testing.T) { 499 e := et.NewEngine(api.CoreFeaturesV1).(*engine) 500 _, err := e.NewModuleEngine("foo", 501 &wasm.Module{}, 502 nil, // imports 503 nil, // moduleFunctions 504 ) 505 require.EqualError(t, err, "source module for foo must be compiled before instantiation") 506 }) 507 t.Run("fail", func(t *testing.T) { 508 e := et.NewEngine(api.CoreFeaturesV1).(*engine) 509 510 errModule := &wasm.Module{ 511 TypeSection: []*wasm.FunctionType{{}}, 512 FunctionSection: []wasm.Index{0, 0, 0}, 513 CodeSection: []*wasm.Code{ 514 {Body: []byte{wasm.OpcodeEnd}}, 515 {Body: []byte{wasm.OpcodeEnd}}, 516 {Body: []byte{wasm.OpcodeCall}}, // Call instruction without immediate for call target index is invalid and should fail to compile. 517 }, 518 ID: wasm.ModuleID{}, 519 } 520 errModule.BuildFunctionDefinitions() 521 522 err := e.CompileModule(testCtx, errModule, nil) 523 require.EqualError(t, err, "failed to lower func[.$2] to wazeroir: handling instruction: apply stack failed for call: reading immediates: EOF") 524 525 // On the compilation failure, all the compiled functions including succeeded ones must be released. 526 _, ok := e.codes[errModule.ID] 527 require.False(t, ok) 528 }) 529 t.Run("ok", func(t *testing.T) { 530 e := et.NewEngine(api.CoreFeaturesV1).(*engine) 531 532 okModule := &wasm.Module{ 533 TypeSection: []*wasm.FunctionType{{}}, 534 FunctionSection: []wasm.Index{0, 0, 0, 0}, 535 CodeSection: []*wasm.Code{ 536 {Body: []byte{wasm.OpcodeEnd}}, 537 {Body: []byte{wasm.OpcodeEnd}}, 538 {Body: []byte{wasm.OpcodeEnd}}, 539 {Body: []byte{wasm.OpcodeEnd}}, 540 }, 541 ID: wasm.ModuleID{}, 542 } 543 err := e.CompileModule(testCtx, okModule, nil) 544 require.NoError(t, err) 545 546 compiled, ok := e.codes[okModule.ID] 547 require.True(t, ok) 548 require.Equal(t, len(okModule.FunctionSection), len(compiled)) 549 550 _, ok = e.codes[okModule.ID] 551 require.True(t, ok) 552 }) 553 } 554 555 func TestEngine_CachedcodesPerModule(t *testing.T) { 556 e := et.NewEngine(api.CoreFeaturesV1).(*engine) 557 exp := []*code{ 558 {body: []*interpreterOp{}}, 559 {body: []*interpreterOp{}}, 560 } 561 m := &wasm.Module{} 562 563 e.addCodes(m, exp) 564 565 actual, ok := e.getCodes(m) 566 require.True(t, ok) 567 require.Equal(t, len(exp), len(actual)) 568 for i := range actual { 569 require.Equal(t, exp[i], actual[i]) 570 } 571 572 e.deleteCodes(m) 573 _, ok = e.getCodes(m) 574 require.False(t, ok) 575 }