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