github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/integration_test/engine/adhoc_test.go (about) 1 package adhoc 2 3 import ( 4 "bytes" 5 "context" 6 _ "embed" 7 "errors" 8 "fmt" 9 "math" 10 "runtime" 11 "strconv" 12 "strings" 13 "testing" 14 "time" 15 "unsafe" 16 17 "github.com/tetratelabs/wazero" 18 "github.com/tetratelabs/wazero/api" 19 "github.com/tetratelabs/wazero/experimental" 20 "github.com/tetratelabs/wazero/experimental/logging" 21 "github.com/tetratelabs/wazero/experimental/table" 22 "github.com/tetratelabs/wazero/internal/leb128" 23 "github.com/tetratelabs/wazero/internal/platform" 24 "github.com/tetratelabs/wazero/internal/testing/binaryencoding" 25 "github.com/tetratelabs/wazero/internal/testing/proxy" 26 "github.com/tetratelabs/wazero/internal/testing/require" 27 "github.com/tetratelabs/wazero/internal/wasm" 28 "github.com/tetratelabs/wazero/internal/wasm/binary" 29 "github.com/tetratelabs/wazero/internal/wasmdebug" 30 "github.com/tetratelabs/wazero/internal/wasmruntime" 31 "github.com/tetratelabs/wazero/sys" 32 ) 33 34 type testCase struct { 35 f func(t *testing.T, r wazero.Runtime) 36 } 37 38 var tests = map[string]testCase{ 39 "huge call stack unwind": {f: testHugeCallStackUnwind}, 40 "imported mutable global": {f: testImportedMutableGlobalUpdate}, 41 "huge stack": {f: testHugeStack}, 42 "unreachable": {f: testUnreachable}, 43 "recursive entry": {f: testRecursiveEntry}, 44 "host func memory": {f: testHostFuncMemory}, 45 "host function with context parameter": {f: testHostFunctionContextParameter}, 46 "host function with nested context": {f: testNestedGoContext}, 47 "host function with numeric parameter": {f: testHostFunctionNumericParameter}, 48 "close module with in-flight calls": {f: testCloseInFlight}, 49 "multiple instantiation from same source": {f: testMultipleInstantiation}, 50 "exported function that grows memory": {f: testMemOps}, 51 "import functions with reference type in signature": {f: testReftypeImports}, 52 "overflow integer addition": {f: testOverflow}, 53 "un-signed extend global": {f: testGlobalExtend}, 54 "user-defined primitive in host func": {f: testUserDefinedPrimitiveHostFunc}, 55 "ensures invocations terminate on module close": {f: testEnsureTerminationOnClose}, 56 "call host function indirectly": {f: callHostFunctionIndirect}, 57 "lookup function": {f: testLookupFunction}, 58 "memory grow in recursive call": {f: testMemoryGrowInRecursiveCall}, 59 "call": {f: testCall}, 60 "module memory": {f: testModuleMemory}, 61 "two indirection to host": {f: testTwoIndirection}, 62 "before listener globals": {f: testBeforeListenerGlobals}, 63 "before listener stack iterator": {f: testBeforeListenerStackIterator}, 64 "before listener stack iterator offsets": {f: testListenerStackIteratorOffset}, 65 "many params many results / doubler": {f: testManyParamsResultsDoubler}, 66 "many params many results / doubler / listener": {f: testManyParamsResultsDoublerListener}, 67 "many params many results / call_many_consts": {f: testManyParamsResultsCallManyConsts}, 68 "many params many results / call_many_consts / listener": {f: testManyParamsResultsCallManyConstsListener}, 69 "many params many results / swapper": {f: testManyParamsResultsSwapper}, 70 "many params many results / swapper / listener": {f: testManyParamsResultsSwapperListener}, 71 "many params many results / main": {f: testManyParamsResultsMain}, 72 "many params many results / main / listener": {f: testManyParamsResultsMainListener}, 73 "many params many results / call_many_consts_and_pick_last_vector": {f: testManyParamsResultsCallManyConstsAndPickLastVector}, 74 "many params many results / call_many_consts_and_pick_last_vector / listener": {f: testManyParamsResultsCallManyConstsAndPickLastVectorListener}, 75 "close table importing module": {f: testCloseTableImportingModule}, 76 "close table exporting module": {f: testCloseTableExportingModule}, 77 "huge binary": {f: testHugeBinary}, 78 } 79 80 func TestEngineCompiler(t *testing.T) { 81 if !platform.CompilerSupported() { 82 t.Skip() 83 } 84 runAllTests(t, tests, wazero.NewRuntimeConfigCompiler().WithCloseOnContextDone(true), false) 85 } 86 87 func TestEngineInterpreter(t *testing.T) { 88 runAllTests(t, tests, wazero.NewRuntimeConfigInterpreter().WithCloseOnContextDone(true), false) 89 } 90 91 // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors. 92 var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary") 93 94 const i32, i64, f32, f64, v128 = wasm.ValueTypeI32, wasm.ValueTypeI64, wasm.ValueTypeF32, wasm.ValueTypeF64, wasm.ValueTypeV128 95 96 var memoryCapacityPages = uint32(2) 97 98 func runAllTests(t *testing.T, tests map[string]testCase, config wazero.RuntimeConfig, isWazevo bool) { 99 for name, tc := range tests { 100 name := name 101 tc := tc 102 t.Run(name, func(t *testing.T) { 103 t.Parallel() 104 tc.f(t, wazero.NewRuntimeWithConfig(testCtx, config)) 105 }) 106 } 107 } 108 109 var ( 110 //go:embed testdata/unreachable.wasm 111 unreachableWasm []byte 112 //go:embed testdata/recursive.wasm 113 recursiveWasm []byte 114 //go:embed testdata/host_memory.wasm 115 hostMemoryWasm []byte 116 //go:embed testdata/hugestack.wasm 117 hugestackWasm []byte 118 //go:embed testdata/memory.wasm 119 memoryWasm []byte 120 //go:embed testdata/reftype_imports.wasm 121 reftypeImportsWasm []byte 122 //go:embed testdata/overflow.wasm 123 overflowWasm []byte 124 //go:embed testdata/global_extend.wasm 125 globalExtendWasm []byte 126 //go:embed testdata/infinite_loop.wasm 127 infiniteLoopWasm []byte 128 //go:embed testdata/huge_call_stack_unwind.wasm 129 hugeCallStackUnwind []byte 130 ) 131 132 func testEnsureTerminationOnClose(t *testing.T, r wazero.Runtime) { 133 compiled, err := r.CompileModule(context.Background(), infiniteLoopWasm) 134 require.NoError(t, err) 135 136 newInfiniteLoopFn := func(t *testing.T) (m api.Module, infinite api.Function) { 137 var err error 138 m, err = r.InstantiateModule(context.Background(), compiled, wazero.NewModuleConfig().WithName(t.Name())) 139 require.NoError(t, err) 140 infinite = m.ExportedFunction("infinite_loop") 141 require.NotNil(t, infinite) 142 return 143 } 144 145 t.Run("context cancel", func(t *testing.T) { 146 _, infinite := newInfiniteLoopFn(t) 147 ctx, cancel := context.WithCancel(context.Background()) 148 go func() { 149 time.Sleep(time.Second) 150 cancel() 151 }() 152 _, err = infinite.Call(ctx) 153 require.Error(t, err) 154 require.Contains(t, err.Error(), "module closed with context canceled") 155 }) 156 157 t.Run("context cancel in advance", func(t *testing.T) { 158 _, infinite := newInfiniteLoopFn(t) 159 ctx, cancel := context.WithCancel(context.Background()) 160 cancel() 161 _, err = infinite.Call(ctx) 162 require.Error(t, err) 163 require.Contains(t, err.Error(), "module closed with context canceled") 164 }) 165 166 t.Run("context timeout", func(t *testing.T) { 167 _, infinite := newInfiniteLoopFn(t) 168 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 169 defer cancel() 170 _, err = infinite.Call(ctx) 171 require.Error(t, err) 172 require.Contains(t, err.Error(), "module closed with context deadline exceeded") 173 }) 174 175 t.Run("explicit close of module", func(t *testing.T) { 176 module, infinite := newInfiniteLoopFn(t) 177 go func() { 178 time.Sleep(time.Second) 179 require.NoError(t, module.CloseWithExitCode(context.Background(), 2)) 180 }() 181 _, err = infinite.Call(context.Background()) 182 require.Error(t, err) 183 require.Contains(t, err.Error(), "module closed with exit_code(2)") 184 }) 185 } 186 187 func testUserDefinedPrimitiveHostFunc(t *testing.T, r wazero.Runtime) { 188 type u32 uint32 189 type u64 uint64 190 type f32 float32 191 type f64 float64 192 193 const fn = "fn" 194 hostCompiled, err := r.NewHostModuleBuilder("host").NewFunctionBuilder(). 195 WithFunc(func(u1 u32, u2 u64, f1 f32, f2 f64) u64 { 196 return u64(u1) + u2 + u64(math.Float32bits(float32(f1))) + u64(math.Float64bits(float64(f2))) 197 }).Export(fn).Compile(testCtx) 198 require.NoError(t, err) 199 200 _, err = r.InstantiateModule(testCtx, hostCompiled, wazero.NewModuleConfig()) 201 require.NoError(t, err) 202 203 proxyBin := proxy.NewModuleBinary("host", hostCompiled) 204 205 mod, err := r.Instantiate(testCtx, proxyBin) 206 require.NoError(t, err) 207 208 f := mod.ExportedFunction(fn) 209 require.NotNil(t, f) 210 211 const u1, u2, f1, f2 = 1, 2, float32(1234.123), 5431.123 212 res, err := f.Call(context.Background(), uint64(u1), uint64(u2), uint64(math.Float32bits(f1)), math.Float64bits(f2)) 213 require.NoError(t, err) 214 require.Equal(t, res[0], uint64(u1)+uint64(u2)+uint64(math.Float32bits(f1))+math.Float64bits(f2)) 215 } 216 217 func testReftypeImports(t *testing.T, r wazero.Runtime) { 218 type dog struct { 219 name string 220 } 221 222 hostObj := &dog{name: "hello"} 223 host, err := r.NewHostModuleBuilder("host"). 224 NewFunctionBuilder(). 225 WithFunc(func(ctx context.Context, externrefFromRefNull uintptr) uintptr { 226 require.Zero(t, externrefFromRefNull) 227 return uintptr(unsafe.Pointer(hostObj)) 228 }). 229 Export("externref"). 230 Instantiate(testCtx) 231 require.NoError(t, err) 232 defer func() { 233 require.NoError(t, host.Close(testCtx)) 234 }() 235 236 module, err := r.Instantiate(testCtx, reftypeImportsWasm) 237 require.NoError(t, err) 238 defer func() { 239 require.NoError(t, module.Close(testCtx)) 240 }() 241 242 actual, err := module.ExportedFunction("get_externref_by_host").Call(testCtx) 243 require.NoError(t, err) 244 245 // Verifies that the returned raw uintptr is the same as the one for the host object. 246 require.Equal(t, uintptr(unsafe.Pointer(hostObj)), uintptr(actual[0])) 247 } 248 249 func testHugeStack(t *testing.T, r wazero.Runtime) { 250 module, err := r.Instantiate(testCtx, hugestackWasm) 251 require.NoError(t, err) 252 defer func() { 253 require.NoError(t, module.Close(testCtx)) 254 }() 255 256 verifyResult := func(t *testing.T, res []uint64) { 257 const resultNumInUint64 = 180 258 require.Equal(t, resultNumInUint64, len(res)) 259 for i := uint64(1); i <= resultNumInUint64; i++ { 260 require.Equal(t, i, res[i-1]) 261 } 262 } 263 264 t.Run("main", func(t *testing.T) { 265 fn := module.ExportedFunction("main") 266 require.NotNil(t, fn) 267 res, err := fn.Call(testCtx, 0, 0, 0, 0, 0, 0) // params ignored by wasm 268 require.NoError(t, err) 269 verifyResult(t, res) 270 }) 271 272 t.Run("memory_fill_after_main", func(t *testing.T) { 273 fn := module.ExportedFunction("memory_fill_after_main") 274 require.NotNil(t, fn) 275 for _, offset := range []uint64{0, 2, 4, 8, 16, 32, 48, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768} { 276 for _, size := range []uint64{0, 2, 4, 8, 16, 32, 48, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384} { 277 t.Run(fmt.Sprintf("offset=%d,size=%d", offset, size), func(t *testing.T) { 278 res, err := fn.Call(testCtx, offset, 0xff, size) 279 require.NoError(t, err) 280 verifyResult(t, res) 281 }) 282 } 283 } 284 }) 285 } 286 287 // testOverflow ensures that adding one into the maximum integer results in the 288 // minimum one. See #636. 289 func testOverflow(t *testing.T, r wazero.Runtime) { 290 module, err := r.Instantiate(testCtx, overflowWasm) 291 require.NoError(t, err) 292 defer func() { 293 require.NoError(t, module.Close(testCtx)) 294 }() 295 296 for _, name := range []string{"i32", "i64"} { 297 i32 := module.ExportedFunction(name) 298 require.NotNil(t, i32) 299 300 res, err := i32.Call(testCtx) 301 require.NoError(t, err) 302 303 require.Equal(t, uint64(1), res[0]) 304 } 305 } 306 307 // testGlobalExtend ensures that un-signed extension of i32 globals must be zero extended. See #656. 308 func testGlobalExtend(t *testing.T, r wazero.Runtime) { 309 module, err := r.Instantiate(testCtx, globalExtendWasm) 310 require.NoError(t, err) 311 defer func() { 312 require.NoError(t, module.Close(testCtx)) 313 }() 314 315 extend := module.ExportedFunction("extend") 316 require.NotNil(t, extend) 317 318 res, err := extend.Call(testCtx) 319 require.NoError(t, err) 320 321 require.Equal(t, uint64(0xffff_ffff), res[0]) 322 } 323 324 func testUnreachable(t *testing.T, r wazero.Runtime) { 325 callUnreachable := func() { 326 panic("panic in host function") 327 } 328 329 _, err := r.NewHostModuleBuilder("host"). 330 NewFunctionBuilder().WithFunc(callUnreachable).Export("cause_unreachable"). 331 Instantiate(testCtx) 332 require.NoError(t, err) 333 334 module, err := r.Instantiate(testCtx, unreachableWasm) 335 require.NoError(t, err) 336 defer func() { 337 require.NoError(t, module.Close(testCtx)) 338 }() 339 340 _, err = module.ExportedFunction("main").Call(testCtx) 341 exp := `panic in host function (recovered by wazero) 342 wasm stack trace: 343 host.cause_unreachable() 344 .two() 345 .one() 346 .main()` 347 require.Equal(t, exp, err.Error()) 348 } 349 350 func testRecursiveEntry(t *testing.T, r wazero.Runtime) { 351 hostfunc := func(ctx context.Context, mod api.Module) { 352 _, err := mod.ExportedFunction("called_by_host_func").Call(testCtx) 353 require.NoError(t, err) 354 } 355 356 _, err := r.NewHostModuleBuilder("env"). 357 NewFunctionBuilder().WithFunc(hostfunc).Export("host_func"). 358 Instantiate(testCtx) 359 require.NoError(t, err) 360 361 module, err := r.Instantiate(testCtx, recursiveWasm) 362 require.NoError(t, err) 363 defer func() { 364 require.NoError(t, module.Close(testCtx)) 365 }() 366 367 _, err = module.ExportedFunction("main").Call(testCtx, 1) 368 require.NoError(t, err) 369 } 370 371 // testHostFuncMemory ensures that host functions can see the callers' memory 372 func testHostFuncMemory(t *testing.T, r wazero.Runtime) { 373 var memory *wasm.MemoryInstance 374 storeInt := func(ctx context.Context, m api.Module, offset uint32, val uint64) uint32 { 375 if !m.Memory().WriteUint64Le(offset, val) { 376 return 1 377 } 378 // sneak a reference to the memory, so we can check it later 379 memory = m.Memory().(*wasm.MemoryInstance) 380 return 0 381 } 382 383 host, err := r.NewHostModuleBuilder("host"). 384 NewFunctionBuilder().WithFunc(storeInt).Export("store_int"). 385 Instantiate(testCtx) 386 require.NoError(t, err) 387 defer func() { 388 require.NoError(t, host.Close(testCtx)) 389 }() 390 391 module, err := r.Instantiate(testCtx, hostMemoryWasm) 392 require.NoError(t, err) 393 defer func() { 394 require.NoError(t, module.Close(testCtx)) 395 }() 396 397 // Call store_int and ensure it didn't return an error code. 398 fn := module.ExportedFunction("store_int") 399 results, err := fn.Call(testCtx, 1, math.MaxUint64) 400 require.NoError(t, err) 401 require.Equal(t, uint64(0), results[0]) 402 403 // Since offset=1 and val=math.MaxUint64, we expect to have written exactly 8 bytes, with all bits set, at index 1. 404 require.Equal(t, []byte{0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0}, memory.Buffer[0:10]) 405 } 406 407 // testNestedGoContext ensures context is updated when a function calls another. 408 func testNestedGoContext(t *testing.T, r wazero.Runtime) { 409 nestedCtx := context.WithValue(context.Background(), struct{}{}, "nested") 410 411 importedName := t.Name() + "-imported" 412 importingName := t.Name() + "-importing" 413 414 var importing api.Module 415 416 imported, err := r.NewHostModuleBuilder(importedName). 417 NewFunctionBuilder(). 418 WithFunc(func(ctx context.Context, p uint32) uint32 { 419 // We expect the initial context, testCtx, to be overwritten by "outer" when it called this. 420 require.Equal(t, nestedCtx, ctx) 421 return p + 1 422 }). 423 Export("inner"). 424 NewFunctionBuilder(). 425 WithFunc(func(ctx context.Context, module api.Module, p uint32) uint32 { 426 require.Equal(t, testCtx, ctx) 427 results, err := module.ExportedFunction("inner").Call(nestedCtx, uint64(p)) 428 require.NoError(t, err) 429 return uint32(results[0]) + 1 430 }). 431 Export("outer"). 432 Instantiate(testCtx) 433 require.NoError(t, err) 434 defer func() { 435 require.NoError(t, imported.Close(testCtx)) 436 }() 437 438 // Instantiate a module that uses Wasm code to call the host function. 439 importing, err = r.Instantiate(testCtx, callOuterInnerWasm(t, importedName, importingName)) 440 require.NoError(t, err) 441 defer func() { 442 require.NoError(t, importing.Close(testCtx)) 443 }() 444 445 input := uint64(math.MaxUint32 - 2) // We expect two calls where each increment by one. 446 results, err := importing.ExportedFunction("call->outer").Call(testCtx, input) 447 require.NoError(t, err) 448 require.Equal(t, uint64(math.MaxUint32), results[0]) 449 } 450 451 // testHostFunctionContextParameter ensures arg0 is optionally a context. 452 func testHostFunctionContextParameter(t *testing.T, r wazero.Runtime) { 453 importedName := t.Name() + "-imported" 454 importingName := t.Name() + "-importing" 455 456 var importing api.Module 457 fns := map[string]interface{}{ 458 "ctx": func(ctx context.Context, p uint32) uint32 { 459 require.Equal(t, testCtx, ctx) 460 return p + 1 461 }, 462 "ctx mod": func(ctx context.Context, module api.Module, p uint32) uint32 { 463 require.Equal(t, importing, module) 464 return p + 1 465 }, 466 } 467 468 for test := range fns { 469 t.Run(test, func(t *testing.T) { 470 imported, err := r.NewHostModuleBuilder(importedName). 471 NewFunctionBuilder().WithFunc(fns[test]).Export("return_input"). 472 Instantiate(testCtx) 473 require.NoError(t, err) 474 defer func() { 475 require.NoError(t, imported.Close(testCtx)) 476 }() 477 478 // Instantiate a module that uses Wasm code to call the host function. 479 importing, err = r.Instantiate(testCtx, 480 callReturnImportWasm(t, importedName, importingName, i32)) 481 require.NoError(t, err) 482 defer func() { 483 require.NoError(t, importing.Close(testCtx)) 484 }() 485 486 results, err := importing.ExportedFunction("call_return_input").Call(testCtx, math.MaxUint32-1) 487 require.NoError(t, err) 488 require.Equal(t, uint64(math.MaxUint32), results[0]) 489 }) 490 } 491 } 492 493 // testHostFunctionNumericParameter ensures numeric parameters aren't corrupted 494 func testHostFunctionNumericParameter(t *testing.T, r wazero.Runtime) { 495 importedName := t.Name() + "-imported" 496 importingName := t.Name() + "-importing" 497 498 fns := map[string]interface{}{ 499 "i32": func(ctx context.Context, p uint32) uint32 { 500 return p + 1 501 }, 502 "i64": func(ctx context.Context, p uint64) uint64 { 503 return p + 1 504 }, 505 "f32": func(ctx context.Context, p float32) float32 { 506 return p + 1 507 }, 508 "f64": func(ctx context.Context, p float64) float64 { 509 return p + 1 510 }, 511 } 512 513 for _, test := range []struct { 514 name string 515 vt wasm.ValueType 516 input, expected uint64 517 }{ 518 { 519 name: "i32", 520 vt: i32, 521 input: math.MaxUint32 - 1, 522 expected: math.MaxUint32, 523 }, 524 { 525 name: "i64", 526 vt: i64, 527 input: math.MaxUint64 - 1, 528 expected: math.MaxUint64, 529 }, 530 { 531 name: "f32", 532 vt: wasm.ValueTypeF32, 533 input: api.EncodeF32(math.MaxFloat32 - 1), 534 expected: api.EncodeF32(math.MaxFloat32), 535 }, 536 { 537 name: "f64", 538 vt: wasm.ValueTypeF64, 539 input: api.EncodeF64(math.MaxFloat64 - 1), 540 expected: api.EncodeF64(math.MaxFloat64), 541 }, 542 } { 543 t.Run(test.name, func(t *testing.T) { 544 imported, err := r.NewHostModuleBuilder(importedName). 545 NewFunctionBuilder().WithFunc(fns[test.name]).Export("return_input"). 546 Instantiate(testCtx) 547 require.NoError(t, err) 548 defer func() { 549 require.NoError(t, imported.Close(testCtx)) 550 }() 551 552 // Instantiate a module that uses Wasm code to call the host function. 553 importing, err := r.Instantiate(testCtx, 554 callReturnImportWasm(t, importedName, importingName, test.vt)) 555 require.NoError(t, err) 556 defer func() { 557 require.NoError(t, importing.Close(testCtx)) 558 }() 559 560 results, err := importing.ExportedFunction("call_return_input").Call(testCtx, test.input) 561 require.NoError(t, err) 562 require.Equal(t, test.expected, results[0]) 563 }) 564 } 565 } 566 567 func callHostFunctionIndirect(t *testing.T, r wazero.Runtime) { 568 // With the following call graph, 569 // originWasmModule -- call --> importingWasmModule -- call --> hostModule 570 // this ensures that hostModule's hostFn only has access importingWasmModule, not originWasmModule. 571 572 const hostModule, importingWasmModule, originWasmModule = "host", "importing", "origin" 573 const hostFn, importingWasmModuleFn, originModuleFn = "host_fn", "call_host_func", "origin" 574 importingModule := &wasm.Module{ 575 TypeSection: []wasm.FunctionType{{Params: []wasm.ValueType{}, Results: []wasm.ValueType{}}}, 576 ImportSection: []wasm.Import{{Module: hostModule, Name: hostFn, Type: wasm.ExternTypeFunc, DescFunc: 0}}, 577 FunctionSection: []wasm.Index{0}, 578 ExportSection: []wasm.Export{{Name: importingWasmModuleFn, Type: wasm.ExternTypeFunc, Index: 1}}, 579 CodeSection: []wasm.Code{{Body: []byte{wasm.OpcodeCall, 0, wasm.OpcodeEnd}}}, 580 NameSection: &wasm.NameSection{ModuleName: importingWasmModule}, 581 } 582 583 originModule := &wasm.Module{ 584 TypeSection: []wasm.FunctionType{{Params: []wasm.ValueType{}, Results: []wasm.ValueType{}}}, 585 ImportSection: []wasm.Import{{Module: importingWasmModule, Name: importingWasmModuleFn, Type: wasm.ExternTypeFunc, DescFunc: 0}}, 586 FunctionSection: []wasm.Index{0}, 587 ExportSection: []wasm.Export{{Name: "origin", Type: wasm.ExternTypeFunc, Index: 1}}, 588 CodeSection: []wasm.Code{{Body: []byte{wasm.OpcodeCall, 0, wasm.OpcodeEnd}}}, 589 NameSection: &wasm.NameSection{ModuleName: originWasmModule}, 590 } 591 592 require.NoError(t, importingModule.Validate(api.CoreFeaturesV2)) 593 require.NoError(t, originModule.Validate(api.CoreFeaturesV2)) 594 importingModuleBytes := binaryencoding.EncodeModule(importingModule) 595 originModuleBytes := binaryencoding.EncodeModule(originModule) 596 597 var originInst, importingInst api.Module 598 _, err := r.NewHostModuleBuilder(hostModule). 599 NewFunctionBuilder(). 600 WithFunc(func(ctx context.Context, mod api.Module) { 601 // Module must be the caller (importing module), not the origin. 602 require.Equal(t, mod, importingInst) 603 require.NotEqual(t, mod, originInst) 604 // Name must be the caller, not origin. 605 require.Equal(t, importingWasmModule, mod.Name()) 606 }). 607 Export(hostFn). 608 Instantiate(testCtx) 609 require.NoError(t, err) 610 611 importingInst, err = r.Instantiate(testCtx, importingModuleBytes) 612 require.NoError(t, err) 613 originInst, err = r.Instantiate(testCtx, originModuleBytes) 614 require.NoError(t, err) 615 616 originFn := originInst.ExportedFunction(originModuleFn) 617 require.NotNil(t, originFn) 618 619 _, err = originFn.Call(testCtx) 620 require.NoError(t, err) 621 } 622 623 func callReturnImportWasm(t *testing.T, importedModule, importingModule string, vt wasm.ValueType) []byte { 624 // test an imported function by re-exporting it 625 module := &wasm.Module{ 626 TypeSection: []wasm.FunctionType{{Params: []wasm.ValueType{vt}, Results: []wasm.ValueType{vt}}}, 627 // (import "%[2]s" "return_input" (func $return_input (param i32) (result i32))) 628 ImportSection: []wasm.Import{ 629 {Module: importedModule, Name: "return_input", Type: wasm.ExternTypeFunc, DescFunc: 0}, 630 }, 631 FunctionSection: []wasm.Index{0}, 632 ExportSection: []wasm.Export{ 633 // (export "return_input" (func $return_input)) 634 {Name: "return_input", Type: wasm.ExternTypeFunc, Index: 0}, 635 // (export "call_return_input" (func $call_return_input)) 636 {Name: "call_return_input", Type: wasm.ExternTypeFunc, Index: 1}, 637 }, 638 // (func $call_return_input (param i32) (result i32) local.get 0 call $return_input) 639 CodeSection: []wasm.Code{ 640 {Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeCall, 0, wasm.OpcodeEnd}}, 641 }, 642 NameSection: &wasm.NameSection{ 643 ModuleName: importingModule, 644 FunctionNames: wasm.NameMap{ 645 {Index: 0, Name: "return_input"}, 646 {Index: 1, Name: "call_return_input"}, 647 }, 648 }, 649 } 650 require.NoError(t, module.Validate(api.CoreFeaturesV2)) 651 return binaryencoding.EncodeModule(module) 652 } 653 654 func callOuterInnerWasm(t *testing.T, importedModule, importingModule string) []byte { 655 module := &wasm.Module{ 656 TypeSection: []wasm.FunctionType{{Params: []wasm.ValueType{i32}, Results: []wasm.ValueType{i32}}}, 657 // (import "%[2]s" "outer" (func $outer (param i32) (result i32))) 658 // (import "%[2]s" "inner" (func $inner (param i32) (result i32))) 659 ImportSection: []wasm.Import{ 660 {Module: importedModule, Name: "outer", Type: wasm.ExternTypeFunc, DescFunc: 0}, 661 {Module: importedModule, Name: "inner", Type: wasm.ExternTypeFunc, DescFunc: 0}, 662 }, 663 FunctionSection: []wasm.Index{0, 0}, 664 ExportSection: []wasm.Export{ 665 // (export "call->outer" (func $call_outer)) 666 {Name: "call->outer", Type: wasm.ExternTypeFunc, Index: 2}, 667 // (export "inner" (func $call_inner)) 668 {Name: "inner", Type: wasm.ExternTypeFunc, Index: 3}, 669 }, 670 CodeSection: []wasm.Code{ 671 // (func $call_outer (param i32) (result i32) local.get 0 call $outer) 672 {Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeCall, 0, wasm.OpcodeEnd}}, 673 // (func $call_inner (param i32) (result i32) local.get 0 call $inner) 674 {Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeCall, 1, wasm.OpcodeEnd}}, 675 }, 676 NameSection: &wasm.NameSection{ 677 ModuleName: importingModule, 678 FunctionNames: wasm.NameMap{ 679 {Index: 0, Name: "outer"}, 680 {Index: 1, Name: "inner"}, 681 {Index: 2, Name: "call_outer"}, 682 {Index: 3, Name: "call_inner"}, 683 }, 684 }, 685 } 686 require.NoError(t, module.Validate(api.CoreFeaturesV2)) 687 return binaryencoding.EncodeModule(module) 688 } 689 690 func testCloseInFlight(t *testing.T, r wazero.Runtime) { 691 tests := []struct { 692 name, function string 693 closeImporting, closeImported uint32 694 closeImportingCode, closeImportedCode bool 695 }{ 696 { // e.g. WASI proc_exit or AssemblyScript abort handler. 697 name: "importing", 698 function: "call_return_input", 699 closeImporting: 1, 700 }, 701 // TODO: A module that re-exports a function (ex "return_input") can call it after it is closed! 702 { // e.g. A function that stops the runtime. 703 name: "both", 704 function: "call_return_input", 705 closeImporting: 1, 706 closeImported: 2, 707 }, 708 { // e.g. WASI proc_exit or AssemblyScript abort handler. 709 name: "importing", 710 function: "call_return_input", 711 closeImporting: 1, 712 closeImportedCode: true, 713 }, 714 { // e.g. WASI proc_exit or AssemblyScript abort handler. 715 name: "importing", 716 function: "call_return_input", 717 closeImporting: 1, 718 closeImportedCode: true, 719 closeImportingCode: true, 720 }, 721 // TODO: A module that re-exports a function (ex "return_input") can call it after it is closed! 722 { // e.g. A function that stops the runtime. 723 name: "both", 724 function: "call_return_input", 725 closeImporting: 1, 726 closeImported: 2, 727 closeImportingCode: true, 728 }, 729 } 730 for _, tt := range tests { 731 tc := tt 732 733 t.Run(tc.name, func(t *testing.T) { 734 var importingCode, importedCode wazero.CompiledModule 735 var imported, importing api.Module 736 var err error 737 closeAndReturn := func(ctx context.Context, x uint32) uint32 { 738 if tc.closeImporting != 0 { 739 require.NoError(t, importing.CloseWithExitCode(ctx, tc.closeImporting)) 740 } 741 if tc.closeImported != 0 { 742 require.NoError(t, imported.CloseWithExitCode(ctx, tc.closeImported)) 743 } 744 if tc.closeImportedCode { 745 err = importedCode.Close(testCtx) 746 require.NoError(t, err) 747 } 748 if tc.closeImportingCode { 749 err = importingCode.Close(testCtx) 750 require.NoError(t, err) 751 } 752 return x 753 } 754 755 // Create the host module, which exports the function that closes the importing module. 756 importedCode, err = r.NewHostModuleBuilder(t.Name() + "-imported"). 757 NewFunctionBuilder().WithFunc(closeAndReturn).Export("return_input"). 758 Compile(testCtx) 759 require.NoError(t, err) 760 761 imported, err = r.InstantiateModule(testCtx, importedCode, wazero.NewModuleConfig()) 762 require.NoError(t, err) 763 defer func() { 764 require.NoError(t, imported.Close(testCtx)) 765 }() 766 767 // Import that module. 768 bin := callReturnImportWasm(t, imported.Name(), t.Name()+"-importing", i32) 769 importingCode, err = r.CompileModule(testCtx, bin) 770 require.NoError(t, err) 771 772 importing, err = r.InstantiateModule(testCtx, importingCode, wazero.NewModuleConfig()) 773 require.NoError(t, err) 774 defer func() { 775 require.NoError(t, importing.Close(testCtx)) 776 }() 777 778 var expectedErr error 779 if tc.closeImported != 0 && tc.closeImporting != 0 { 780 // When both modules are closed, importing is the better one to choose in the error message. 781 expectedErr = sys.NewExitError(tc.closeImporting) 782 } else if tc.closeImported != 0 { 783 expectedErr = sys.NewExitError(tc.closeImported) 784 } else if tc.closeImporting != 0 { 785 expectedErr = sys.NewExitError(tc.closeImporting) 786 } else { 787 t.Fatal("invalid test case") 788 } 789 790 // Functions that return after being closed should have an exit error. 791 _, err = importing.ExportedFunction(tc.function).Call(testCtx, 5) 792 require.Equal(t, expectedErr, err) 793 }) 794 } 795 } 796 797 func testMemOps(t *testing.T, r wazero.Runtime) { 798 // Instantiate a module that manages its memory 799 mod, err := r.Instantiate(testCtx, memoryWasm) 800 require.NoError(t, err) 801 defer func() { 802 require.NoError(t, mod.Close(testCtx)) 803 }() 804 805 // Check the export worked 806 require.Equal(t, mod.Memory(), mod.ExportedMemory("memory")) 807 memory := mod.Memory() 808 809 sizeFn, storeFn, growFn := mod.ExportedFunction("size"), mod.ExportedFunction("store"), mod.ExportedFunction("grow") 810 811 // Check the size command worked 812 results, err := sizeFn.Call(testCtx) 813 require.NoError(t, err) 814 require.Zero(t, results[0]) 815 require.Zero(t, memory.Size()) 816 817 // Any offset should be out of bounds error even when it is less than memory capacity(=memoryCapacityPages). 818 _, err = storeFn.Call(testCtx, wasm.MemoryPagesToBytesNum(memoryCapacityPages)-8) 819 require.Error(t, err) // Out of bounds error. 820 821 // Try to grow the memory by one page 822 results, err = growFn.Call(testCtx, 1) 823 require.NoError(t, err) 824 require.Zero(t, results[0]) // should succeed and return the old size in pages. 825 826 // Any offset larger than the current size should be out of bounds error even when it is less than memory capacity. 827 _, err = storeFn.Call(testCtx, wasm.MemoryPagesToBytesNum(memoryCapacityPages)-8) 828 require.Error(t, err) // Out of bounds error. 829 830 // Check the size command works! 831 results, err = sizeFn.Call(testCtx) 832 require.NoError(t, err) 833 require.Equal(t, uint64(1), results[0]) // 1 page 834 require.Equal(t, uint32(65536), memory.Size()) // 64KB 835 836 // Grow again so that the memory size matches memory capacity. 837 results, err = growFn.Call(testCtx, 1) 838 require.NoError(t, err) 839 require.Equal(t, uint64(1), results[0]) 840 841 // Verify the size matches cap. 842 results, err = sizeFn.Call(testCtx) 843 require.NoError(t, err) 844 require.Equal(t, uint64(memoryCapacityPages), results[0]) 845 846 // Now the store instruction at the memory capcity bound should succeed. 847 _, err = storeFn.Call(testCtx, wasm.MemoryPagesToBytesNum(memoryCapacityPages)-8) // i64.store needs 8 bytes from offset. 848 require.NoError(t, err) 849 } 850 851 func testMultipleInstantiation(t *testing.T, r wazero.Runtime) { 852 bin := binaryencoding.EncodeModule(&wasm.Module{ 853 TypeSection: []wasm.FunctionType{{}}, 854 FunctionSection: []wasm.Index{0}, 855 MemorySection: &wasm.Memory{Min: 1, Cap: 1, Max: 1, IsMaxEncoded: true}, 856 CodeSection: []wasm.Code{{ 857 Body: []byte{ 858 wasm.OpcodeI32Const, 1, // i32.const 1 ;; memory offset 859 wasm.OpcodeI64Const, 0xe8, 0x7, // i64.const 1000 ;; expected value 860 wasm.OpcodeI64Store, 0x3, 0x0, // i64.store 861 wasm.OpcodeEnd, 862 }, 863 }}, 864 ExportSection: []wasm.Export{{Name: "store"}}, 865 }) 866 compiled, err := r.CompileModule(testCtx, bin) 867 require.NoError(t, err) 868 defer func() { 869 require.NoError(t, compiled.Close(testCtx)) 870 }() 871 872 // Instantiate multiple modules with the same source (*CompiledModule). 873 for i := 0; i < 100; i++ { 874 module, err := r.InstantiateModule(testCtx, compiled, wazero.NewModuleConfig().WithName(strconv.Itoa(i))) 875 require.NoError(t, err) 876 877 // Ensure that compilation cache doesn't cause race on memory instance. 878 before, ok := module.Memory().ReadUint64Le(1) 879 require.True(t, ok) 880 // Value must be zero as the memory must not be affected by the previously instantiated modules. 881 require.Zero(t, before) 882 883 f := module.ExportedFunction("store") 884 require.NotNil(t, f) 885 886 _, err = f.Call(testCtx) 887 require.NoError(t, err) 888 889 // After the call, the value must be set properly. 890 after, ok := module.Memory().ReadUint64Le(1) 891 require.True(t, ok) 892 require.Equal(t, uint64(1000), after) 893 894 require.NoError(t, module.Close(testCtx)) 895 } 896 } 897 898 func testLookupFunction(t *testing.T, r wazero.Runtime) { 899 bin := binaryencoding.EncodeModule(&wasm.Module{ 900 TypeSection: []wasm.FunctionType{{Results: []wasm.ValueType{i32}}}, 901 FunctionSection: []wasm.Index{0, 0, 0}, 902 CodeSection: []wasm.Code{ 903 {Body: []byte{wasm.OpcodeI32Const, 1, wasm.OpcodeEnd}}, 904 {Body: []byte{wasm.OpcodeI32Const, 2, wasm.OpcodeEnd}}, 905 {Body: []byte{wasm.OpcodeI32Const, 3, wasm.OpcodeEnd}}, 906 }, 907 TableSection: []wasm.Table{{Min: 10, Type: wasm.RefTypeFuncref}}, 908 ElementSection: []wasm.ElementSegment{ 909 { 910 OffsetExpr: wasm.ConstantExpression{ 911 Opcode: wasm.OpcodeI32Const, 912 Data: []byte{0}, 913 }, 914 TableIndex: 0, 915 Init: []wasm.Index{2, 0}, 916 }, 917 }, 918 }) 919 920 inst, err := r.Instantiate(testCtx, bin) 921 require.NoError(t, err) 922 923 t.Run("null reference", func(t *testing.T) { 924 err = require.CapturePanic(func() { 925 table.LookupFunction(inst, 0, 3, nil, []wasm.ValueType{i32}) 926 }) 927 require.Equal(t, wasmruntime.ErrRuntimeInvalidTableAccess, err) 928 }) 929 930 t.Run("out of range", func(t *testing.T) { 931 err = require.CapturePanic(func() { 932 table.LookupFunction(inst, 0, 1000, nil, []wasm.ValueType{i32}) 933 }) 934 require.Equal(t, wasmruntime.ErrRuntimeInvalidTableAccess, err) 935 }) 936 937 t.Run("type mismatch", func(t *testing.T) { 938 err = require.CapturePanic(func() { 939 table.LookupFunction(inst, 0, 0, []wasm.ValueType{i32}, nil) 940 }) 941 require.Equal(t, wasmruntime.ErrRuntimeIndirectCallTypeMismatch, err) 942 }) 943 t.Run("ok", func(t *testing.T) { 944 f2 := table.LookupFunction(inst, 0, 0, nil, []wasm.ValueType{i32}) 945 res, err := f2.Call(testCtx) 946 require.NoError(t, err) 947 require.Equal(t, uint64(3), res[0]) 948 949 f0 := table.LookupFunction(inst, 0, 1, nil, []wasm.ValueType{i32}) 950 res, err = f0.Call(testCtx) 951 require.NoError(t, err) 952 require.Equal(t, uint64(1), res[0]) 953 }) 954 } 955 956 func testMemoryGrowInRecursiveCall(t *testing.T, r wazero.Runtime) { 957 const hostModuleName = "env" 958 const hostFnName = "grow_memory" 959 var growFn api.Function 960 hostCompiled, err := r.NewHostModuleBuilder(hostModuleName).NewFunctionBuilder(). 961 WithFunc(func() { 962 // Does the recursive call into Wasm, which grows memory. 963 _, err := growFn.Call(testCtx) 964 require.NoError(t, err) 965 }).Export(hostFnName).Compile(testCtx) 966 require.NoError(t, err) 967 968 _, err = r.InstantiateModule(testCtx, hostCompiled, wazero.NewModuleConfig()) 969 require.NoError(t, err) 970 971 bin := binaryencoding.EncodeModule(&wasm.Module{ 972 ImportFunctionCount: 1, 973 TypeSection: []wasm.FunctionType{{Params: []wasm.ValueType{}, Results: []wasm.ValueType{}}}, 974 FunctionSection: []wasm.Index{0, 0}, 975 CodeSection: []wasm.Code{ 976 { 977 Body: []byte{ 978 // Calls the imported host function, which in turn calls the next in-Wasm function recursively. 979 wasm.OpcodeCall, 0, 980 // Access the memory and this should succeed as we already had memory grown at this point. 981 wasm.OpcodeI32Const, 0, 982 wasm.OpcodeI32Load, 0x2, 0x0, 983 wasm.OpcodeDrop, 984 wasm.OpcodeEnd, 985 }, 986 }, 987 { 988 // Grows memory by 1 page. 989 Body: []byte{wasm.OpcodeI32Const, 1, wasm.OpcodeMemoryGrow, 0, wasm.OpcodeDrop, wasm.OpcodeEnd}, 990 }, 991 }, 992 MemorySection: &wasm.Memory{Max: 1000}, 993 ImportSection: []wasm.Import{{Module: hostModuleName, Name: hostFnName, DescFunc: 0}}, 994 ImportPerModule: map[string][]*wasm.Import{hostModuleName: {{Module: hostModuleName, Name: hostFnName, DescFunc: 0}}}, 995 ExportSection: []wasm.Export{ 996 {Name: "main", Type: wasm.ExternTypeFunc, Index: 1}, 997 {Name: "grow_memory", Type: wasm.ExternTypeFunc, Index: 2}, 998 }, 999 }) 1000 1001 inst, err := r.Instantiate(testCtx, bin) 1002 require.NoError(t, err) 1003 1004 growFn = inst.ExportedFunction("grow_memory") 1005 require.NotNil(t, growFn) 1006 main := inst.ExportedFunction("main") 1007 require.NotNil(t, main) 1008 1009 _, err = main.Call(testCtx) 1010 require.NoError(t, err) 1011 } 1012 1013 func testCall(t *testing.T, r wazero.Runtime) { 1014 // Define a basic function which defines two parameters and two results. 1015 // This is used to test results when incorrect arity is used. 1016 bin := binaryencoding.EncodeModule(&wasm.Module{ 1017 TypeSection: []wasm.FunctionType{ 1018 { 1019 Params: []wasm.ValueType{i64, i64}, 1020 Results: []wasm.ValueType{i64, i64}, 1021 ParamNumInUint64: 2, 1022 ResultNumInUint64: 2, 1023 }, 1024 }, 1025 FunctionSection: []wasm.Index{0}, 1026 CodeSection: []wasm.Code{ 1027 {Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 1, wasm.OpcodeEnd}}, 1028 }, 1029 ExportSection: []wasm.Export{{Name: "func", Type: wasm.ExternTypeFunc, Index: 0}}, 1030 }) 1031 1032 inst, err := r.Instantiate(testCtx, bin) 1033 require.NoError(t, err) 1034 1035 // Ensure the base case doesn't fail: A single parameter should work as that matches the function signature. 1036 f := inst.ExportedFunction("func") 1037 require.NotNil(t, f) 1038 1039 t.Run("call with stack", func(t *testing.T) { 1040 stack := []uint64{1, 2} 1041 err = f.CallWithStack(testCtx, stack) 1042 require.NoError(t, err) 1043 require.Equal(t, []uint64{1, 2}, stack) 1044 1045 t.Run("errs when not enough parameters", func(t *testing.T) { 1046 err = f.CallWithStack(testCtx, nil) 1047 require.EqualError(t, err, "need 2 params, but stack size is 0") 1048 }) 1049 }) 1050 1051 t.Run("errs when not enough parameters", func(t *testing.T) { 1052 _, err = f.Call(testCtx) 1053 require.EqualError(t, err, "expected 2 params, but passed 0") 1054 }) 1055 1056 t.Run("errs when too many parameters", func(t *testing.T) { 1057 _, err = f.Call(testCtx, 1, 2, 3) 1058 require.EqualError(t, err, "expected 2 params, but passed 3") 1059 }) 1060 } 1061 1062 // RunTestModuleEngineMemory shows that the byte slice returned from api.Memory Read is not a copy, rather a re-slice 1063 // of the underlying memory. This allows both host and Wasm to see each other's writes, unless one side changes the 1064 // capacity of the slice. 1065 // 1066 // Known cases that change the slice capacity: 1067 // * Host code calls append on a byte slice returned by api.Memory Read 1068 // * Wasm code calls wasm.OpcodeMemoryGrowName and this changes the capacity (by default, it will). 1069 func testModuleMemory(t *testing.T, r wazero.Runtime) { 1070 wasmPhrase := "Well, that'll be the day when you say goodbye." 1071 wasmPhraseSize := uint32(len(wasmPhrase)) 1072 1073 one := uint32(1) 1074 1075 bin := binaryencoding.EncodeModule(&wasm.Module{ 1076 TypeSection: []wasm.FunctionType{{Params: []api.ValueType{api.ValueTypeI32}, ParamNumInUint64: 1}, {}}, 1077 FunctionSection: []wasm.Index{0, 1}, 1078 MemorySection: &wasm.Memory{Min: 1, Cap: 1, Max: 2}, 1079 DataSection: []wasm.DataSegment{ 1080 { 1081 Passive: true, 1082 Init: []byte(wasmPhrase), 1083 }, 1084 }, 1085 DataCountSection: &one, 1086 CodeSection: []wasm.Code{ 1087 {Body: []byte{ // "grow" 1088 wasm.OpcodeLocalGet, 0, // how many pages to grow (param) 1089 wasm.OpcodeMemoryGrow, 0, // memory index zero 1090 wasm.OpcodeDrop, // drop the previous page count (or -1 if grow failed) 1091 wasm.OpcodeEnd, 1092 }}, 1093 {Body: []byte{ // "init" 1094 wasm.OpcodeI32Const, 0, // target offset 1095 wasm.OpcodeI32Const, 0, // source offset 1096 wasm.OpcodeI32Const, byte(wasmPhraseSize), // len 1097 wasm.OpcodeMiscPrefix, wasm.OpcodeMiscMemoryInit, 0, 0, // segment 0, memory 0 1098 wasm.OpcodeEnd, 1099 }}, 1100 }, 1101 ExportSection: []wasm.Export{ 1102 {Name: "grow", Type: wasm.ExternTypeFunc, Index: 0}, 1103 {Name: "init", Type: wasm.ExternTypeFunc, Index: 1}, 1104 }, 1105 }) 1106 1107 inst, err := r.Instantiate(testCtx, bin) 1108 require.NoError(t, err) 1109 1110 memory := inst.Memory() 1111 1112 buf, ok := memory.Read(0, wasmPhraseSize) 1113 require.True(t, ok) 1114 require.Equal(t, make([]byte, wasmPhraseSize), buf) 1115 1116 // Initialize the memory using Wasm. This copies the test phrase. 1117 initCallEngine := inst.ExportedFunction("init") 1118 _, err = initCallEngine.Call(testCtx) 1119 require.NoError(t, err) 1120 1121 // We expect the same []byte read earlier to now include the phrase in wasm. 1122 require.Equal(t, wasmPhrase, string(buf)) 1123 1124 hostPhrase := "Goodbye, cruel world. I'm off to join the circus." // Intentionally slightly longer. 1125 hostPhraseSize := uint32(len(hostPhrase)) 1126 1127 // Copy over the buffer, which should stop at the current length. 1128 copy(buf, hostPhrase) 1129 require.Equal(t, "Goodbye, cruel world. I'm off to join the circ", string(buf)) 1130 1131 // The underlying memory should be updated. This proves that Memory.Read returns a re-slice, not a copy, and that 1132 // programs can rely on this (for example, to update shared state in Wasm and view that in Go and visa versa). 1133 buf2, ok := memory.Read(0, wasmPhraseSize) 1134 require.True(t, ok) 1135 require.Equal(t, buf, buf2) 1136 1137 // Now, append to the buffer we got from Wasm. As this changes capacity, it should result in a new byte slice. 1138 buf = append(buf, 'u', 's', '.') 1139 require.Equal(t, hostPhrase, string(buf)) 1140 1141 // To prove the above, we re-read the memory and should not see the appended bytes (rather zeros instead). 1142 buf2, ok = memory.Read(0, hostPhraseSize) 1143 require.True(t, ok) 1144 hostPhraseTruncated := "Goodbye, cruel world. I'm off to join the circ" + string([]byte{0, 0, 0}) 1145 require.Equal(t, hostPhraseTruncated, string(buf2)) 1146 1147 // Now, we need to prove the other direction, that when Wasm changes the capacity, the host's buffer is unaffected. 1148 growCallEngine := inst.ExportedFunction("grow") 1149 _, err = growCallEngine.Call(testCtx, 1) 1150 require.NoError(t, err) 1151 1152 // The host buffer should still contain the same bytes as before grow 1153 require.Equal(t, hostPhraseTruncated, string(buf2)) 1154 1155 // Re-initialize the memory in wasm, which overwrites the region. 1156 initCallEngine2 := inst.ExportedFunction("init") 1157 _, err = initCallEngine2.Call(testCtx) 1158 require.NoError(t, err) 1159 1160 // The host was not affected because it is a different slice due to "memory.grow" affecting the underlying memory. 1161 require.Equal(t, hostPhraseTruncated, string(buf2)) 1162 } 1163 1164 func testTwoIndirection(t *testing.T, r wazero.Runtime) { 1165 var buf bytes.Buffer 1166 ctx := experimental.WithFunctionListenerFactory(testCtx, logging.NewLoggingListenerFactory(&buf)) 1167 _, err := r.NewHostModuleBuilder("host").NewFunctionBuilder().WithFunc(func( 1168 _ context.Context, m api.Module, d uint32, 1169 ) uint32 { 1170 if d == math.MaxUint32 { 1171 panic(errors.New("host-function panic")) 1172 } else if d == math.MaxUint32-1 { 1173 err := m.CloseWithExitCode(context.Background(), 1) 1174 require.NoError(t, err) 1175 panic(errors.New("host-function panic")) 1176 } 1177 return 1 / d // panics if d ==0. 1178 }).Export("div").Instantiate(ctx) 1179 require.NoError(t, err) 1180 1181 ft := wasm.FunctionType{Params: []wasm.ValueType{i32}, Results: []wasm.ValueType{i32}} 1182 hostImporter := binaryencoding.EncodeModule(&wasm.Module{ 1183 ImportSection: []wasm.Import{{Module: "host", Name: "div", DescFunc: 0}}, 1184 TypeSection: []wasm.FunctionType{ft}, 1185 FunctionSection: []wasm.Index{0}, 1186 CodeSection: []wasm.Code{ 1187 // Calling imported host function ^. 1188 {Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeCall, 0, wasm.OpcodeEnd}}, 1189 }, 1190 ExportSection: []wasm.Export{{Name: "call_host_div", Type: wasm.ExternTypeFunc, Index: 1}}, 1191 NameSection: &wasm.NameSection{ 1192 ModuleName: "host_importer", 1193 FunctionNames: wasm.NameMap{{Index: wasm.Index(1), Name: "call_host_div"}}, 1194 }, 1195 }) 1196 1197 _, err = r.Instantiate(ctx, hostImporter) 1198 require.NoError(t, err) 1199 1200 main := binaryencoding.EncodeModule(&wasm.Module{ 1201 ImportFunctionCount: 1, 1202 TypeSection: []wasm.FunctionType{ft}, 1203 ImportSection: []wasm.Import{{Module: "host_importer", Name: "call_host_div", DescFunc: 0}}, 1204 FunctionSection: []wasm.Index{0}, 1205 CodeSection: []wasm.Code{{Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeCall, 0, wasm.OpcodeEnd}}}, 1206 ExportSection: []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 1}}, 1207 NameSection: &wasm.NameSection{ModuleName: "main", FunctionNames: wasm.NameMap{{Index: wasm.Index(1), Name: "main"}}}, 1208 }) 1209 1210 inst, err := r.Instantiate(ctx, main) 1211 require.NoError(t, err) 1212 1213 t.Run("ok", func(t *testing.T) { 1214 mainFn := inst.ExportedFunction("main") 1215 require.NotNil(t, mainFn) 1216 1217 result1, err := mainFn.Call(testCtx, 1) 1218 require.NoError(t, err) 1219 1220 result2, err := mainFn.Call(testCtx, 2) 1221 require.NoError(t, err) 1222 1223 require.Equal(t, uint64(1), result1[0]) 1224 require.Equal(t, uint64(0), result2[0]) 1225 }) 1226 1227 t.Run("errors", func(t *testing.T) { 1228 for _, tc := range []struct { 1229 name string 1230 input uint64 1231 expErr string 1232 }{ 1233 {name: "host panic", input: math.MaxUint32, expErr: `host-function panic (recovered by wazero) 1234 wasm stack trace: 1235 host.div(i32) i32 1236 host_importer.call_host_div(i32) i32 1237 main.main(i32) i32`}, 1238 {name: "go runtime panic", input: 0, expErr: `runtime error: integer divide by zero (recovered by wazero) 1239 wasm stack trace: 1240 host.div(i32) i32 1241 host_importer.call_host_div(i32) i32 1242 main.main(i32) i32`}, 1243 {name: "module closed and then go runtime panic", input: math.MaxUint32 - 1, expErr: `host-function panic (recovered by wazero) 1244 wasm stack trace: 1245 host.div(i32) i32 1246 host_importer.call_host_div(i32) i32 1247 main.main(i32) i32`}, 1248 } { 1249 tc := tc 1250 t.Run(tc.name, func(t *testing.T) { 1251 mainFn := inst.ExportedFunction("main") 1252 require.NotNil(t, mainFn) 1253 1254 _, err := mainFn.Call(testCtx, tc.input) 1255 require.Error(t, err) 1256 errStr := err.Error() 1257 // If this faces a Go runtime error, the error includes the Go stack trace which makes the test unstable, 1258 // so we trim them here. 1259 if index := strings.Index(errStr, wasmdebug.GoRuntimeErrorTracePrefix); index > -1 { 1260 errStr = strings.TrimSpace(errStr[:index]) 1261 } 1262 require.Equal(t, errStr, tc.expErr) 1263 }) 1264 } 1265 }) 1266 1267 require.Equal(t, ` 1268 --> main.main(1) 1269 --> host_importer.call_host_div(1) 1270 ==> host.div(1) 1271 <== 1 1272 <-- 1 1273 <-- 1 1274 --> main.main(2) 1275 --> host_importer.call_host_div(2) 1276 ==> host.div(2) 1277 <== 0 1278 <-- 0 1279 <-- 0 1280 --> main.main(-1) 1281 --> host_importer.call_host_div(-1) 1282 ==> host.div(-1) 1283 --> main.main(0) 1284 --> host_importer.call_host_div(0) 1285 ==> host.div(0) 1286 --> main.main(-2) 1287 --> host_importer.call_host_div(-2) 1288 ==> host.div(-2) 1289 `, "\n"+buf.String()) 1290 } 1291 1292 func testBeforeListenerGlobals(t *testing.T, r wazero.Runtime) { 1293 type globals struct { 1294 values []uint64 1295 types []api.ValueType 1296 } 1297 1298 expectedGlobals := []globals{ 1299 {values: []uint64{100, 200}, types: []api.ValueType{api.ValueTypeI32, api.ValueTypeI32}}, 1300 {values: []uint64{42, 11}, types: []api.ValueType{api.ValueTypeI32, api.ValueTypeI32}}, 1301 } 1302 1303 fnListener := &fnListener{ 1304 beforeFn: func(ctx context.Context, mod api.Module, def api.FunctionDefinition, params []uint64, si experimental.StackIterator) { 1305 require.True(t, len(expectedGlobals) > 0) 1306 1307 imod := mod.(experimental.InternalModule) 1308 expected := expectedGlobals[0] 1309 1310 require.Equal(t, len(expected.values), imod.NumGlobal()) 1311 for i := 0; i < imod.NumGlobal(); i++ { 1312 global := imod.Global(i) 1313 require.Equal(t, expected.types[i], global.Type()) 1314 require.Equal(t, expected.values[i], global.Get()) 1315 } 1316 1317 expectedGlobals = expectedGlobals[1:] 1318 }, 1319 } 1320 1321 buf := binaryencoding.EncodeModule(&wasm.Module{ 1322 TypeSection: []wasm.FunctionType{{}}, 1323 FunctionSection: []wasm.Index{0, 0}, 1324 GlobalSection: []wasm.Global{ 1325 { 1326 Type: wasm.GlobalType{ValType: wasm.ValueTypeI32, Mutable: true}, 1327 Init: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(100)}, 1328 }, 1329 { 1330 Type: wasm.GlobalType{ValType: wasm.ValueTypeI32, Mutable: true}, 1331 Init: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(200)}, 1332 }, 1333 }, 1334 CodeSection: []wasm.Code{ 1335 { 1336 Body: []byte{ 1337 wasm.OpcodeI32Const, 42, 1338 wasm.OpcodeGlobalSet, 0, // store 42 in global 0 1339 wasm.OpcodeI32Const, 11, 1340 wasm.OpcodeGlobalSet, 1, // store 11 in global 1 1341 wasm.OpcodeCall, 1, // call f2 1342 wasm.OpcodeEnd, 1343 }, 1344 }, 1345 {Body: []byte{wasm.OpcodeEnd}}, 1346 }, 1347 ExportSection: []wasm.Export{{Name: "f", Type: wasm.ExternTypeFunc, Index: 0}}, 1348 }) 1349 1350 ctx := experimental.WithFunctionListenerFactory(testCtx, fnListener) 1351 inst, err := r.Instantiate(ctx, buf) 1352 require.NoError(t, err) 1353 1354 f := inst.ExportedFunction("f") 1355 require.NotNil(t, f) 1356 1357 _, err = f.Call(ctx) 1358 require.NoError(t, err) 1359 require.True(t, len(expectedGlobals) == 0) 1360 } 1361 1362 // testBeforeListenerStackIterator tests that the StackIterator provided by the Engine to the Before hook 1363 // of the listener is properly able to walk the stack. 1364 func testBeforeListenerStackIterator(t *testing.T, r wazero.Runtime) { 1365 type stackEntry struct { 1366 debugName string 1367 } 1368 1369 expectedCallstacks := [][]stackEntry{ 1370 { // when calling f1 1371 {debugName: "whatever.f1"}, 1372 }, 1373 { // when calling f2 1374 {debugName: "whatever.f2"}, 1375 {debugName: "whatever.f1"}, 1376 }, 1377 { // when calling 1378 {debugName: "whatever.f3"}, 1379 {debugName: "whatever.f2"}, 1380 {debugName: "whatever.f1"}, 1381 }, 1382 { // when calling f4 1383 {debugName: "host.f4"}, 1384 {debugName: "whatever.f3"}, 1385 {debugName: "whatever.f2"}, 1386 {debugName: "whatever.f1"}, 1387 }, 1388 } 1389 1390 fnListener := &fnListener{ 1391 beforeFn: func(ctx context.Context, mod api.Module, def api.FunctionDefinition, params []uint64, si experimental.StackIterator) { 1392 require.True(t, len(expectedCallstacks) > 0) 1393 expectedCallstack := expectedCallstacks[0] 1394 for si.Next() { 1395 require.True(t, len(expectedCallstack) > 0) 1396 require.Equal(t, expectedCallstack[0].debugName, si.Function().Definition().DebugName()) 1397 expectedCallstack = expectedCallstack[1:] 1398 } 1399 require.Equal(t, 0, len(expectedCallstack)) 1400 expectedCallstacks = expectedCallstacks[1:] 1401 }, 1402 } 1403 1404 ctx := experimental.WithFunctionListenerFactory(testCtx, fnListener) 1405 _, err := r.NewHostModuleBuilder("host").NewFunctionBuilder().WithFunc(func(x int32) int32 { 1406 return x + 100 1407 }).Export("f4").Instantiate(ctx) 1408 require.NoError(t, err) 1409 1410 m := binaryencoding.EncodeModule(&wasm.Module{ 1411 TypeSection: []wasm.FunctionType{ 1412 // f1 type 1413 { 1414 Params: []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, 1415 Results: []api.ValueType{}, 1416 }, 1417 // f2 type 1418 { 1419 Params: []api.ValueType{}, 1420 Results: []api.ValueType{api.ValueTypeI32}, 1421 }, 1422 // f3 type 1423 { 1424 Params: []api.ValueType{api.ValueTypeI32}, 1425 Results: []api.ValueType{api.ValueTypeI32}, 1426 }, 1427 // f4 type 1428 { 1429 Params: []api.ValueType{api.ValueTypeI32}, 1430 Results: []api.ValueType{api.ValueTypeI32}, 1431 }, 1432 }, 1433 ImportFunctionCount: 1, 1434 ImportSection: []wasm.Import{{Name: "f4", Module: "host", DescFunc: 3}}, 1435 FunctionSection: []wasm.Index{0, 1, 2}, 1436 NameSection: &wasm.NameSection{ 1437 ModuleName: "whatever", 1438 FunctionNames: wasm.NameMap{ 1439 {Index: wasm.Index(1), Name: "f1"}, 1440 {Index: wasm.Index(2), Name: "f2"}, 1441 {Index: wasm.Index(3), Name: "f3"}, 1442 {Index: wasm.Index(0), Name: "f4"}, 1443 }, 1444 }, 1445 CodeSection: []wasm.Code{ 1446 { // f1 1447 Body: []byte{ 1448 wasm.OpcodeCall, 1449 2, // call f2 1450 wasm.OpcodeDrop, 1451 wasm.OpcodeEnd, 1452 }, 1453 }, 1454 { // f2 1455 LocalTypes: []wasm.ValueType{wasm.ValueTypeI32}, 1456 Body: []byte{ 1457 wasm.OpcodeI32Const, 42, // local for f2 1458 wasm.OpcodeLocalSet, 0, 1459 wasm.OpcodeI32Const, 5, // argument of f3 1460 wasm.OpcodeCall, 1461 3, // call f3 1462 wasm.OpcodeEnd, 1463 }, 1464 }, 1465 { // f3 1466 Body: []byte{ 1467 wasm.OpcodeI32Const, 6, 1468 wasm.OpcodeCall, 1469 0, // call host function 1470 wasm.OpcodeEnd, 1471 }, 1472 }, 1473 }, 1474 ExportSection: []wasm.Export{{Name: "f1", Type: wasm.ExternTypeFunc, Index: 1}}, 1475 }) 1476 1477 inst, err := r.Instantiate(ctx, m) 1478 require.NoError(t, err) 1479 1480 f1 := inst.ExportedFunction("f1") 1481 require.NotNil(t, f1) 1482 1483 _, err = f1.Call(ctx, 2, 3, 4) 1484 require.NoError(t, err) 1485 require.Equal(t, 0, len(expectedCallstacks)) 1486 } 1487 1488 func testListenerStackIteratorOffset(t *testing.T, r wazero.Runtime) { 1489 type frame struct { 1490 function api.FunctionDefinition 1491 offset uint64 1492 } 1493 1494 var tape [][]frame 1495 fnListener := &fnListener{ 1496 beforeFn: func(ctx context.Context, mod api.Module, def api.FunctionDefinition, params []uint64, si experimental.StackIterator) { 1497 var stack []frame 1498 for si.Next() { 1499 fn := si.Function() 1500 pc := si.ProgramCounter() 1501 stack = append(stack, frame{fn.Definition(), fn.SourceOffsetForPC(pc)}) 1502 } 1503 tape = append(tape, stack) 1504 }, 1505 } 1506 ctx := experimental.WithFunctionListenerFactory(testCtx, fnListener) 1507 1508 // Minimal DWARF info section to make debug/dwarf.New() happy. 1509 // Necessary to make the compiler emit source offset maps. 1510 minimalDWARFInfo := []byte{ 1511 0x7, 0x0, 0x0, 0x0, // length (len(info) - 4) 1512 0x3, 0x0, // version (between 3 and 5 makes it easier) 1513 0x0, 0x0, 0x0, 0x0, // abbrev offset 1514 0x0, // asize 1515 } 1516 1517 encoded := binaryencoding.EncodeModule(&wasm.Module{ 1518 TypeSection: []wasm.FunctionType{ 1519 // f1 type 1520 {Params: []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}}, 1521 // f2 type 1522 {Results: []api.ValueType{api.ValueTypeI32}}, 1523 // f3 type 1524 {Params: []api.ValueType{api.ValueTypeI32}, Results: []api.ValueType{api.ValueTypeI32}}, 1525 }, 1526 FunctionSection: []wasm.Index{0, 1, 2}, 1527 NameSection: &wasm.NameSection{ 1528 ModuleName: "whatever", 1529 FunctionNames: wasm.NameMap{ 1530 {Index: wasm.Index(0), Name: "f1"}, 1531 {Index: wasm.Index(1), Name: "f2"}, 1532 {Index: wasm.Index(2), Name: "f3"}, 1533 }, 1534 }, 1535 CodeSection: []wasm.Code{ 1536 { // f1 1537 Body: []byte{ 1538 wasm.OpcodeI32Const, 42, 1539 wasm.OpcodeLocalSet, 0, 1540 wasm.OpcodeI32Const, 11, 1541 wasm.OpcodeLocalSet, 1, 1542 wasm.OpcodeCall, 1, // call f2 1543 wasm.OpcodeDrop, 1544 wasm.OpcodeEnd, 1545 }, 1546 }, 1547 { 1548 Body: []byte{ 1549 wasm.OpcodeI32Const, 6, 1550 wasm.OpcodeCall, 2, // call f3 1551 wasm.OpcodeEnd, 1552 }, 1553 }, 1554 {Body: []byte{wasm.OpcodeI32Const, 15, wasm.OpcodeEnd}}, 1555 }, 1556 ExportSection: []wasm.Export{ 1557 {Name: "f1", Type: wasm.ExternTypeFunc, Index: 0}, 1558 {Name: "f2", Type: wasm.ExternTypeFunc, Index: 1}, 1559 {Name: "f3", Type: wasm.ExternTypeFunc, Index: 2}, 1560 }, 1561 CustomSections: []*wasm.CustomSection{{Name: ".debug_info", Data: minimalDWARFInfo}}, 1562 }) 1563 decoded, err := binary.DecodeModule(encoded, api.CoreFeaturesV2, 0, false, true, true) 1564 require.NoError(t, err) 1565 1566 f1offset := decoded.CodeSection[0].BodyOffsetInCodeSection 1567 f2offset := decoded.CodeSection[1].BodyOffsetInCodeSection 1568 f3offset := decoded.CodeSection[2].BodyOffsetInCodeSection 1569 1570 inst, err := r.Instantiate(ctx, encoded) 1571 require.NoError(t, err) 1572 1573 f1Fn := inst.ExportedFunction("f1") 1574 require.NotNil(t, f1Fn) 1575 1576 _, err = f1Fn.Call(ctx, 2, 3, 4) 1577 require.NoError(t, err) 1578 1579 module, ok := inst.(*wasm.ModuleInstance) 1580 require.True(t, ok) 1581 1582 defs := module.ExportedFunctionDefinitions() 1583 f1 := defs["f1"] 1584 f2 := defs["f2"] 1585 f3 := defs["f3"] 1586 t.Logf("f1 offset: %#x", f1offset) 1587 t.Logf("f2 offset: %#x", f2offset) 1588 t.Logf("f3 offset: %#x", f3offset) 1589 1590 expectedStacks := [][]frame{ 1591 { 1592 {f1, f1offset + 0}, 1593 }, 1594 { 1595 {f2, f2offset + 0}, 1596 {f1, f1offset + 8}, // index of call opcode in f1's code 1597 }, 1598 { 1599 {f3, f3offset}, // host functions don't have a wasm code offset 1600 {f2, f2offset + 2}, // index of call opcode in f2's code 1601 {f1, f1offset + 8}, // index of call opcode in f1's code 1602 }, 1603 } 1604 1605 for si, stack := range tape { 1606 t.Log("Recorded stack", si, ":") 1607 require.True(t, len(expectedStacks) > 0, "more recorded stacks than expected stacks") 1608 expectedStack := expectedStacks[0] 1609 expectedStacks = expectedStacks[1:] 1610 for fi, frame := range stack { 1611 t.Logf("\t%d -> %s :: %#x", fi, frame.function.Name(), frame.offset) 1612 require.True(t, len(expectedStack) > 0, "more frames in stack than expected") 1613 expectedFrame := expectedStack[0] 1614 expectedStack = expectedStack[1:] 1615 require.Equal(t, expectedFrame, frame) 1616 } 1617 require.Zero(t, len(expectedStack), "expected more frames in stack") 1618 } 1619 require.Zero(t, len(expectedStacks), "expected more stacks") 1620 } 1621 1622 // fnListener implements both experimental.FunctionListenerFactory and experimental.FunctionListener for testing. 1623 type fnListener struct { 1624 beforeFn func(context.Context, api.Module, api.FunctionDefinition, []uint64, experimental.StackIterator) 1625 afterFn func(context.Context, api.Module, api.FunctionDefinition, []uint64) 1626 abortFn func(context.Context, api.Module, api.FunctionDefinition, any) 1627 } 1628 1629 // NewFunctionListener implements experimental.FunctionListenerFactory. 1630 func (f *fnListener) NewFunctionListener(api.FunctionDefinition) experimental.FunctionListener { 1631 return f 1632 } 1633 1634 // Before implements experimental.FunctionListener. 1635 func (f *fnListener) Before(ctx context.Context, mod api.Module, def api.FunctionDefinition, params []uint64, stackIterator experimental.StackIterator) { 1636 if f.beforeFn != nil { 1637 f.beforeFn(ctx, mod, def, params, stackIterator) 1638 } 1639 } 1640 1641 // After implements experimental.FunctionListener. 1642 func (f *fnListener) After(ctx context.Context, mod api.Module, def api.FunctionDefinition, results []uint64) { 1643 if f.afterFn != nil { 1644 f.afterFn(ctx, mod, def, results) 1645 } 1646 } 1647 1648 // Abort implements experimental.FunctionListener. 1649 func (f *fnListener) Abort(ctx context.Context, mod api.Module, def api.FunctionDefinition, err error) { 1650 if f.abortFn != nil { 1651 f.abortFn(ctx, mod, def, err) 1652 } 1653 } 1654 1655 func manyParamsResultsMod() (bin []byte, params []uint64) { 1656 mainType := wasm.FunctionType{} 1657 swapperType := wasm.FunctionType{} 1658 doublerType := wasm.FunctionType{} 1659 manyConstsType := wasm.FunctionType{} 1660 callManyConstsType := wasm.FunctionType{} 1661 pickLastVectorType := wasm.FunctionType{Results: []wasm.ValueType{v128}} 1662 callManyConstsAndPickLastVectorType := wasm.FunctionType{Results: []wasm.ValueType{v128}} 1663 for i := 0; i < 20; i++ { 1664 swapperType.Params = append(swapperType.Params, i32, i64, f32, f64, v128) 1665 swapperType.Results = append(swapperType.Results, v128, f64, f32, i64, i32) 1666 mainType.Params = append(mainType.Params, i32, i64, f32, f64, v128) 1667 mainType.Results = append(mainType.Results, v128, f64, f32, i64, i32) 1668 doublerType.Params = append(doublerType.Results, v128, f64, f32, i64, i32) 1669 doublerType.Results = append(doublerType.Results, v128, f64, f32, i64, i32) 1670 manyConstsType.Results = append(manyConstsType.Results, i32, i64, f32, f64, v128) 1671 callManyConstsType.Results = append(callManyConstsType.Results, i32, i64, f32, f64, v128) 1672 pickLastVectorType.Params = append(pickLastVectorType.Params, i32, i64, f32, f64, v128) 1673 } 1674 1675 var mainBody []byte 1676 for i := 0; i < 100; i++ { 1677 mainBody = append(mainBody, wasm.OpcodeLocalGet) 1678 mainBody = append(mainBody, leb128.EncodeUint32(uint32(i))...) 1679 } 1680 mainBody = append(mainBody, wasm.OpcodeCall, 1) // Call swapper. 1681 mainBody = append(mainBody, wasm.OpcodeCall, 2) // Call doubler. 1682 mainBody = append(mainBody, wasm.OpcodeEnd) 1683 1684 var swapperBody []byte 1685 for i := 0; i < 100; i++ { 1686 swapperBody = append(swapperBody, wasm.OpcodeLocalGet) 1687 swapperBody = append(swapperBody, leb128.EncodeUint32(uint32(99-i))...) 1688 } 1689 swapperBody = append(swapperBody, wasm.OpcodeEnd) 1690 1691 var doublerBody []byte 1692 for i := 0; i < 100; i += 5 { 1693 // Returns v128 as-is. 1694 doublerBody = append(doublerBody, wasm.OpcodeLocalGet) 1695 doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i))...) 1696 // Double f64. 1697 doublerBody = append(doublerBody, wasm.OpcodeLocalGet) 1698 doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+1))...) 1699 doublerBody = append(doublerBody, wasm.OpcodeLocalGet) 1700 doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+1))...) 1701 doublerBody = append(doublerBody, wasm.OpcodeF64Add) 1702 // Double f32. 1703 doublerBody = append(doublerBody, wasm.OpcodeLocalGet) 1704 doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+2))...) 1705 doublerBody = append(doublerBody, wasm.OpcodeLocalGet) 1706 doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+2))...) 1707 doublerBody = append(doublerBody, wasm.OpcodeF32Add) 1708 // Double i64. 1709 doublerBody = append(doublerBody, wasm.OpcodeLocalGet) 1710 doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+3))...) 1711 doublerBody = append(doublerBody, wasm.OpcodeLocalGet) 1712 doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+3))...) 1713 doublerBody = append(doublerBody, wasm.OpcodeI64Add) 1714 // Double i32. 1715 doublerBody = append(doublerBody, wasm.OpcodeLocalGet) 1716 doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+4))...) 1717 doublerBody = append(doublerBody, wasm.OpcodeLocalGet) 1718 doublerBody = append(doublerBody, leb128.EncodeUint32(uint32(i+4))...) 1719 doublerBody = append(doublerBody, wasm.OpcodeI32Add) 1720 } 1721 doublerBody = append(doublerBody, wasm.OpcodeEnd) 1722 1723 var manyConstsBody []byte 1724 for i := 0; i < 100; i += 5 { 1725 ib := byte(i) 1726 manyConstsBody = append(manyConstsBody, wasm.OpcodeI32Const) 1727 manyConstsBody = append(manyConstsBody, leb128.EncodeInt32(int32(i))...) 1728 manyConstsBody = append(manyConstsBody, wasm.OpcodeI64Const) 1729 manyConstsBody = append(manyConstsBody, leb128.EncodeInt64(int64(i))...) 1730 manyConstsBody = append(manyConstsBody, wasm.OpcodeF32Const) 1731 manyConstsBody = append(manyConstsBody, ib, ib, ib, ib) 1732 manyConstsBody = append(manyConstsBody, wasm.OpcodeF64Const) 1733 manyConstsBody = append(manyConstsBody, ib, ib, ib, ib, ib, ib, ib, ib) 1734 manyConstsBody = append(manyConstsBody, wasm.OpcodeVecPrefix, wasm.OpcodeVecV128Const) 1735 manyConstsBody = append(manyConstsBody, ib, ib, ib, ib, ib, ib, ib, ib, ib, ib, ib, ib, ib, ib, ib, ib) 1736 } 1737 manyConstsBody = append(manyConstsBody, wasm.OpcodeEnd) 1738 1739 var callManyConstsBody []byte 1740 callManyConstsBody = append(callManyConstsBody, wasm.OpcodeCall, 5, wasm.OpcodeEnd) 1741 1742 var pickLastVector []byte 1743 pickLastVector = append(pickLastVector, wasm.OpcodeLocalGet, 99, wasm.OpcodeEnd) 1744 1745 var callManyConstsAndPickLastVector []byte 1746 callManyConstsAndPickLastVector = append(callManyConstsAndPickLastVector, wasm.OpcodeCall, 5, wasm.OpcodeCall, 6, wasm.OpcodeEnd) 1747 1748 nameSection := wasm.NameSection{} 1749 nameSection.FunctionNames = []wasm.NameAssoc{ 1750 {Index: 0, Name: "main"}, 1751 {Index: 1, Name: "swapper"}, 1752 {Index: 2, Name: "doubler"}, 1753 {Index: 3, Name: "call_many_consts"}, 1754 {Index: 4, Name: "call_many_consts_and_pick_last_vector"}, 1755 {Index: 5, Name: "many_consts"}, 1756 {Index: 6, Name: "pick_last_vector"}, 1757 } 1758 1759 typeSection := []wasm.FunctionType{mainType, swapperType, doublerType, callManyConstsType, callManyConstsAndPickLastVectorType, manyConstsType, pickLastVectorType} 1760 1761 for i, typ := range typeSection { 1762 paramNames := wasm.NameMapAssoc{Index: wasm.Index(i)} 1763 for paramIndex, paramType := range typ.Params { 1764 name := fmt.Sprintf("[%d:%s]", paramIndex, wasm.ValueTypeName(paramType)) 1765 paramNames.NameMap = append(paramNames.NameMap, wasm.NameAssoc{Index: wasm.Index(paramIndex), Name: name}) 1766 } 1767 nameSection.LocalNames = append(nameSection.LocalNames, paramNames) 1768 } 1769 1770 bin = binaryencoding.EncodeModule(&wasm.Module{ 1771 TypeSection: typeSection, 1772 ExportSection: []wasm.Export{ 1773 {Name: "main", Type: wasm.ExternTypeFunc, Index: 0}, 1774 {Name: "swapper", Type: wasm.ExternTypeFunc, Index: 1}, 1775 {Name: "doubler", Type: wasm.ExternTypeFunc, Index: 2}, 1776 {Name: "call_many_consts", Type: wasm.ExternTypeFunc, Index: 3}, 1777 {Name: "call_many_consts_and_pick_last_vector", Type: wasm.ExternTypeFunc, Index: 4}, 1778 }, 1779 FunctionSection: []wasm.Index{0, 1, 2, 3, 4, 5, 6}, 1780 CodeSection: []wasm.Code{ 1781 {Body: mainBody}, 1782 {Body: swapperBody}, 1783 {Body: doublerBody}, 1784 {Body: callManyConstsBody}, 1785 {Body: callManyConstsAndPickLastVector}, 1786 {Body: manyConstsBody}, 1787 {Body: pickLastVector}, 1788 }, 1789 NameSection: &nameSection, 1790 }) 1791 1792 for i := 0; i < 100; i += 5 { 1793 params = append(params, uint64(i)) 1794 params = append(params, uint64(i+1)) 1795 params = append(params, uint64(i+2)) 1796 params = append(params, uint64(i+3)) 1797 // Vector needs two values. 1798 params = append(params, uint64(i+3)) 1799 params = append(params, uint64(i+3)) 1800 } 1801 return 1802 } 1803 1804 func testManyParamsResultsCallManyConsts(t *testing.T, r wazero.Runtime) { 1805 ctx := context.Background() 1806 1807 bin, _ := manyParamsResultsMod() 1808 mod, err := r.Instantiate(ctx, bin) 1809 require.NoError(t, err) 1810 1811 main := mod.ExportedFunction("call_many_consts") 1812 require.NotNil(t, main) 1813 1814 results, err := main.Call(ctx) 1815 require.NoError(t, err) 1816 1817 exp := []uint64{ 1818 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5050505, 0x505050505050505, 0x505050505050505, 1819 0x505050505050505, 0xa, 0xa, 0xa0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 1820 0xf, 0xf, 0xf0f0f0f, 0xf0f0f0f0f0f0f0f, 0xf0f0f0f0f0f0f0f, 0xf0f0f0f0f0f0f0f, 0x14, 0x14, 0x14141414, 1821 0x1414141414141414, 0x1414141414141414, 0x1414141414141414, 0x19, 0x19, 0x19191919, 0x1919191919191919, 1822 0x1919191919191919, 0x1919191919191919, 0x1e, 0x1e, 0x1e1e1e1e, 0x1e1e1e1e1e1e1e1e, 0x1e1e1e1e1e1e1e1e, 1823 0x1e1e1e1e1e1e1e1e, 0x23, 0x23, 0x23232323, 0x2323232323232323, 0x2323232323232323, 0x2323232323232323, 1824 0x28, 0x28, 0x28282828, 0x2828282828282828, 0x2828282828282828, 0x2828282828282828, 0x2d, 0x2d, 0x2d2d2d2d, 1825 0x2d2d2d2d2d2d2d2d, 0x2d2d2d2d2d2d2d2d, 0x2d2d2d2d2d2d2d2d, 0x32, 0x32, 0x32323232, 0x3232323232323232, 1826 0x3232323232323232, 0x3232323232323232, 0x37, 0x37, 0x37373737, 0x3737373737373737, 0x3737373737373737, 1827 0x3737373737373737, 0x3c, 0x3c, 0x3c3c3c3c, 0x3c3c3c3c3c3c3c3c, 0x3c3c3c3c3c3c3c3c, 0x3c3c3c3c3c3c3c3c, 1828 0x41, 0x41, 0x41414141, 0x4141414141414141, 0x4141414141414141, 0x4141414141414141, 0x46, 0x46, 0x46464646, 1829 0x4646464646464646, 0x4646464646464646, 0x4646464646464646, 0x4b, 0x4b, 0x4b4b4b4b, 0x4b4b4b4b4b4b4b4b, 1830 0x4b4b4b4b4b4b4b4b, 0x4b4b4b4b4b4b4b4b, 0x50, 0x50, 0x50505050, 0x5050505050505050, 0x5050505050505050, 1831 0x5050505050505050, 0x55, 0x55, 0x55555555, 0x5555555555555555, 0x5555555555555555, 0x5555555555555555, 1832 0x5a, 0x5a, 0x5a5a5a5a, 0x5a5a5a5a5a5a5a5a, 0x5a5a5a5a5a5a5a5a, 0x5a5a5a5a5a5a5a5a, 0x5f, 0x5f, 0x5f5f5f5f, 1833 0x5f5f5f5f5f5f5f5f, 0x5f5f5f5f5f5f5f5f, 0x5f5f5f5f5f5f5f5f, 1834 } 1835 require.Equal(t, exp, results) 1836 } 1837 1838 func testManyParamsResultsCallManyConstsListener(t *testing.T, r wazero.Runtime) { 1839 var buf bytes.Buffer 1840 ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf)) 1841 1842 bin, _ := manyParamsResultsMod() 1843 mod, err := r.Instantiate(ctx, bin) 1844 require.NoError(t, err) 1845 1846 main := mod.ExportedFunction("call_many_consts") 1847 require.NotNil(t, main) 1848 1849 results, err := main.Call(ctx) 1850 require.NoError(t, err) 1851 1852 require.Equal(t, ` 1853 --> .call_many_consts() 1854 --> .many_consts() 1855 <-- (0,0,0,0,00000000000000000000000000000000,5,5,6.254552e-36,1.766927440712025e-284,05050505050505050505050505050505,10,10,6.6463464e-33,2.6461938652294957e-260,0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a,15,15,7.0533445e-30,3.815736827118017e-236,0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f,20,20,7.47605e-27,5.964208835435795e-212,14141414141414141414141414141414,25,25,7.914983e-24,9.01285756841504e-188,19191919191919191919191919191919,30,30,8.3706784e-21,1.3075051467559279e-163,1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e,35,35,8.843688e-18,2.008776679223492e-139,23232323232323232323232323232323,40,40,9.334581e-15,3.0654356309538037e-115,28282828282828282828282828282828,45,45,9.8439425e-12,4.4759381595361623e-91,2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d,50,50,1.0372377e-08,6.749300603603778e-67,32323232323232323232323232323232,55,55,1.0920506e-05,1.0410273767909543e-42,37373737373737373737373737373737,60,60,0.01148897,1.5306383611560062e-18,3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c,65,65,12.078431,2.2616345098039214e+06,41414141414141414141414141414141,70,70,12689.568,3.5295369653413445e+30,46464646464646464646464646464646,75,75,1.3323083e+07,5.2285141982483265e+54,4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b,80,80,1.3979697e+10,7.556001431015456e+78,50505050505050505050505050505050,85,85,1.4660155e+13,1.1945305291614955e+103,55555555555555555555555555555555,90,90,1.5365222e+16,1.7838867517321418e+127,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a,95,95,1.6095688e+19,2.5673651826636406e+151,5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f) 1856 <-- (0,0,0,0,00000000000000000000000000000000,5,5,6.254552e-36,1.766927440712025e-284,05050505050505050505050505050505,10,10,6.6463464e-33,2.6461938652294957e-260,0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a,15,15,7.0533445e-30,3.815736827118017e-236,0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f,20,20,7.47605e-27,5.964208835435795e-212,14141414141414141414141414141414,25,25,7.914983e-24,9.01285756841504e-188,19191919191919191919191919191919,30,30,8.3706784e-21,1.3075051467559279e-163,1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e,35,35,8.843688e-18,2.008776679223492e-139,23232323232323232323232323232323,40,40,9.334581e-15,3.0654356309538037e-115,28282828282828282828282828282828,45,45,9.8439425e-12,4.4759381595361623e-91,2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d,50,50,1.0372377e-08,6.749300603603778e-67,32323232323232323232323232323232,55,55,1.0920506e-05,1.0410273767909543e-42,37373737373737373737373737373737,60,60,0.01148897,1.5306383611560062e-18,3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c,65,65,12.078431,2.2616345098039214e+06,41414141414141414141414141414141,70,70,12689.568,3.5295369653413445e+30,46464646464646464646464646464646,75,75,1.3323083e+07,5.2285141982483265e+54,4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b,80,80,1.3979697e+10,7.556001431015456e+78,50505050505050505050505050505050,85,85,1.4660155e+13,1.1945305291614955e+103,55555555555555555555555555555555,90,90,1.5365222e+16,1.7838867517321418e+127,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a,95,95,1.6095688e+19,2.5673651826636406e+151,5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f) 1857 `, "\n"+buf.String()) 1858 exp := []uint64{ 1859 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5050505, 0x505050505050505, 0x505050505050505, 1860 0x505050505050505, 0xa, 0xa, 0xa0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 1861 0xf, 0xf, 0xf0f0f0f, 0xf0f0f0f0f0f0f0f, 0xf0f0f0f0f0f0f0f, 0xf0f0f0f0f0f0f0f, 0x14, 0x14, 0x14141414, 1862 0x1414141414141414, 0x1414141414141414, 0x1414141414141414, 0x19, 0x19, 0x19191919, 0x1919191919191919, 1863 0x1919191919191919, 0x1919191919191919, 0x1e, 0x1e, 0x1e1e1e1e, 0x1e1e1e1e1e1e1e1e, 0x1e1e1e1e1e1e1e1e, 1864 0x1e1e1e1e1e1e1e1e, 0x23, 0x23, 0x23232323, 0x2323232323232323, 0x2323232323232323, 0x2323232323232323, 1865 0x28, 0x28, 0x28282828, 0x2828282828282828, 0x2828282828282828, 0x2828282828282828, 0x2d, 0x2d, 0x2d2d2d2d, 1866 0x2d2d2d2d2d2d2d2d, 0x2d2d2d2d2d2d2d2d, 0x2d2d2d2d2d2d2d2d, 0x32, 0x32, 0x32323232, 0x3232323232323232, 1867 0x3232323232323232, 0x3232323232323232, 0x37, 0x37, 0x37373737, 0x3737373737373737, 0x3737373737373737, 1868 0x3737373737373737, 0x3c, 0x3c, 0x3c3c3c3c, 0x3c3c3c3c3c3c3c3c, 0x3c3c3c3c3c3c3c3c, 0x3c3c3c3c3c3c3c3c, 1869 0x41, 0x41, 0x41414141, 0x4141414141414141, 0x4141414141414141, 0x4141414141414141, 0x46, 0x46, 0x46464646, 1870 0x4646464646464646, 0x4646464646464646, 0x4646464646464646, 0x4b, 0x4b, 0x4b4b4b4b, 0x4b4b4b4b4b4b4b4b, 1871 0x4b4b4b4b4b4b4b4b, 0x4b4b4b4b4b4b4b4b, 0x50, 0x50, 0x50505050, 0x5050505050505050, 0x5050505050505050, 1872 0x5050505050505050, 0x55, 0x55, 0x55555555, 0x5555555555555555, 0x5555555555555555, 0x5555555555555555, 1873 0x5a, 0x5a, 0x5a5a5a5a, 0x5a5a5a5a5a5a5a5a, 0x5a5a5a5a5a5a5a5a, 0x5a5a5a5a5a5a5a5a, 0x5f, 0x5f, 0x5f5f5f5f, 1874 0x5f5f5f5f5f5f5f5f, 0x5f5f5f5f5f5f5f5f, 0x5f5f5f5f5f5f5f5f, 1875 } 1876 require.Equal(t, exp, results) 1877 } 1878 1879 func testManyParamsResultsDoubler(t *testing.T, r wazero.Runtime) { 1880 ctx := context.Background() 1881 1882 bin, params := manyParamsResultsMod() 1883 mod, err := r.Instantiate(ctx, bin) 1884 require.NoError(t, err) 1885 1886 main := mod.ExportedFunction("doubler") 1887 require.NotNil(t, main) 1888 1889 results, err := main.Call(ctx, params...) 1890 require.NoError(t, err) 1891 1892 exp := []uint64{ 1893 0x0, 0x1, 0x4, 0x6, 0x6, 0x6, 0x5, 0x6, 0xe, 0x10, 0x10, 0x10, 0xa, 1894 0xb, 0x18, 0x1a, 0x1a, 0x1a, 0xf, 0x10, 0x22, 0x24, 0x24, 0x24, 0x14, 1895 0x15, 0x2c, 0x2e, 0x2e, 0x2e, 0x19, 0x1a, 0x36, 0x38, 0x38, 0x38, 0x1e, 1896 0x1f, 0x40, 0x42, 0x42, 0x42, 0x23, 0x24, 0x4a, 0x4c, 0x4c, 0x4c, 0x28, 1897 0x29, 0x54, 0x56, 0x56, 0x56, 0x2d, 0x2e, 0x5e, 0x60, 0x60, 0x60, 0x32, 1898 0x33, 0x68, 0x6a, 0x6a, 0x6a, 0x37, 0x38, 0x72, 0x74, 0x74, 0x74, 0x3c, 1899 0x3d, 0x7c, 0x7e, 0x7e, 0x7e, 0x41, 0x42, 0x86, 0x88, 0x88, 0x88, 0x46, 1900 0x47, 0x90, 0x92, 0x92, 0x92, 0x4b, 0x4c, 0x9a, 0x9c, 0x9c, 0x9c, 0x50, 1901 0x51, 0xa4, 0xa6, 0xa6, 0xa6, 0x55, 0x56, 0xae, 0xb0, 0xb0, 0xb0, 0x5a, 1902 0x5b, 0xb8, 0xba, 0xba, 0xba, 0x5f, 0x60, 0xc2, 0xc4, 0xc4, 0xc4, 1903 } 1904 require.Equal(t, exp, results) 1905 } 1906 1907 func testManyParamsResultsDoublerListener(t *testing.T, r wazero.Runtime) { 1908 var buf bytes.Buffer 1909 ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf)) 1910 1911 bin, params := manyParamsResultsMod() 1912 mod, err := r.Instantiate(ctx, bin) 1913 require.NoError(t, err) 1914 1915 main := mod.ExportedFunction("doubler") 1916 require.NotNil(t, main) 1917 1918 results, err := main.Call(ctx, params...) 1919 require.NoError(t, err) 1920 1921 require.Equal(t, ` 1922 --> .doubler([0:v128]=00000000000000000000000000000001,[1:f64]=1e-323,[2:f32]=4e-45,[3:i64]=3,[4:i32]=3,[5:v128]=00000000000000050000000000000006,[6:f64]=3.5e-323,[7:f32]=1.1e-44,[8:i64]=8,[9:i32]=8,[10:v128]=000000000000000a000000000000000b,[11:f64]=6e-323,[12:f32]=1.8e-44,[13:i64]=13,[14:i32]=13,[15:v128]=000000000000000f0000000000000010,[16:f64]=8.4e-323,[17:f32]=2.5e-44,[18:i64]=18,[19:i32]=18,[20:v128]=00000000000000140000000000000015,[21:f64]=1.1e-322,[22:f32]=3.2e-44,[23:i64]=23,[24:i32]=23,[25:v128]=0000000000000019000000000000001a,[26:f64]=1.33e-322,[27:f32]=3.9e-44,[28:i64]=28,[29:i32]=28,[30:v128]=000000000000001e000000000000001f,[31:f64]=1.6e-322,[32:f32]=4.6e-44,[33:i64]=33,[34:i32]=33,[35:v128]=00000000000000230000000000000024,[36:f64]=1.83e-322,[37:f32]=5.3e-44,[38:i64]=38,[39:i32]=38,[40:v128]=00000000000000280000000000000029,[41:f64]=2.08e-322,[42:f32]=6e-44,[43:i64]=43,[44:i32]=43,[45:v128]=000000000000002d000000000000002e,[46:f64]=2.3e-322,[47:f32]=6.7e-44,[48:i64]=48,[49:i32]=48,[50:v128]=00000000000000320000000000000033,[51:f64]=2.57e-322,[52:f32]=7.4e-44,[53:i64]=53,[54:i32]=53,[55:v128]=00000000000000370000000000000038,[56:f64]=2.8e-322,[57:f32]=8.1e-44,[58:i64]=58,[59:i32]=58,[60:v128]=000000000000003c000000000000003d,[61:f64]=3.06e-322,[62:f32]=8.8e-44,[63:i64]=63,[64:i32]=63,[65:v128]=00000000000000410000000000000042,[66:f64]=3.3e-322,[67:f32]=9.5e-44,[68:i64]=68,[69:i32]=68,[70:v128]=00000000000000460000000000000047,[71:f64]=3.56e-322,[72:f32]=1.02e-43,[73:i64]=73,[74:i32]=73,[75:v128]=000000000000004b000000000000004c,[76:f64]=3.8e-322,[77:f32]=1.1e-43,[78:i64]=78,[79:i32]=78,[80:v128]=00000000000000500000000000000051,[81:f64]=4.05e-322,[82:f32]=1.16e-43,[83:i64]=83,[84:i32]=83,[85:v128]=00000000000000550000000000000056,[86:f64]=4.3e-322,[87:f32]=1.23e-43,[88:i64]=88,[89:i32]=88,[90:v128]=000000000000005a000000000000005b,[91:f64]=4.55e-322,[92:f32]=1.3e-43,[93:i64]=93,[94:i32]=93,[95:v128]=000000000000005f0000000000000060,[96:f64]=4.8e-322,[97:f32]=1.37e-43,[98:i64]=98,[99:i32]=98) 1923 <-- (00000000000000000000000000000001,2e-323,8e-45,6,6,00000000000000050000000000000006,7e-323,2.2e-44,16,16,000000000000000a000000000000000b,1.2e-322,3.6e-44,26,26,000000000000000f0000000000000010,1.7e-322,5e-44,36,36,00000000000000140000000000000015,2.17e-322,6.4e-44,46,46,0000000000000019000000000000001a,2.67e-322,7.8e-44,56,56,000000000000001e000000000000001f,3.16e-322,9.2e-44,66,66,00000000000000230000000000000024,3.66e-322,1.06e-43,76,76,00000000000000280000000000000029,4.15e-322,1.2e-43,86,86,000000000000002d000000000000002e,4.64e-322,1.35e-43,96,96,00000000000000320000000000000033,5.14e-322,1.49e-43,106,106,00000000000000370000000000000038,5.63e-322,1.63e-43,116,116,000000000000003c000000000000003d,6.13e-322,1.77e-43,126,126,00000000000000410000000000000042,6.6e-322,1.9e-43,136,136,00000000000000460000000000000047,7.1e-322,2.05e-43,146,146,000000000000004b000000000000004c,7.6e-322,2.19e-43,156,156,00000000000000500000000000000051,8.1e-322,2.33e-43,166,166,00000000000000550000000000000056,8.6e-322,2.47e-43,176,176,000000000000005a000000000000005b,9.1e-322,2.6e-43,186,186,000000000000005f0000000000000060,9.6e-322,2.75e-43,196,196) 1924 `, "\n"+buf.String()) 1925 1926 exp := []uint64{ 1927 0x0, 0x1, 0x4, 0x6, 0x6, 0x6, 0x5, 0x6, 0xe, 0x10, 0x10, 0x10, 0xa, 1928 0xb, 0x18, 0x1a, 0x1a, 0x1a, 0xf, 0x10, 0x22, 0x24, 0x24, 0x24, 0x14, 1929 0x15, 0x2c, 0x2e, 0x2e, 0x2e, 0x19, 0x1a, 0x36, 0x38, 0x38, 0x38, 0x1e, 1930 0x1f, 0x40, 0x42, 0x42, 0x42, 0x23, 0x24, 0x4a, 0x4c, 0x4c, 0x4c, 0x28, 1931 0x29, 0x54, 0x56, 0x56, 0x56, 0x2d, 0x2e, 0x5e, 0x60, 0x60, 0x60, 0x32, 1932 0x33, 0x68, 0x6a, 0x6a, 0x6a, 0x37, 0x38, 0x72, 0x74, 0x74, 0x74, 0x3c, 1933 0x3d, 0x7c, 0x7e, 0x7e, 0x7e, 0x41, 0x42, 0x86, 0x88, 0x88, 0x88, 0x46, 1934 0x47, 0x90, 0x92, 0x92, 0x92, 0x4b, 0x4c, 0x9a, 0x9c, 0x9c, 0x9c, 0x50, 1935 0x51, 0xa4, 0xa6, 0xa6, 0xa6, 0x55, 0x56, 0xae, 0xb0, 0xb0, 0xb0, 0x5a, 1936 0x5b, 0xb8, 0xba, 0xba, 0xba, 0x5f, 0x60, 0xc2, 0xc4, 0xc4, 0xc4, 1937 } 1938 require.Equal(t, exp, results) 1939 } 1940 1941 func testManyParamsResultsSwapper(t *testing.T, r wazero.Runtime) { 1942 ctx := context.Background() 1943 1944 bin, params := manyParamsResultsMod() 1945 mod, err := r.Instantiate(ctx, bin) 1946 require.NoError(t, err) 1947 1948 main := mod.ExportedFunction("swapper") 1949 require.NotNil(t, main) 1950 1951 results, err := main.Call(ctx, params...) 1952 require.NoError(t, err) 1953 1954 exp := []uint64{ 1955 0x62, 0x62, 0x62, 0x61, 0x60, 0x5f, 0x5d, 0x5d, 0x5d, 0x5c, 0x5b, 0x5a, 0x58, 0x58, 0x58, 0x57, 1956 0x56, 0x55, 0x53, 0x53, 0x53, 0x52, 0x51, 0x50, 0x4e, 0x4e, 0x4e, 0x4d, 0x4c, 0x4b, 0x49, 0x49, 1957 0x49, 0x48, 0x47, 0x46, 0x44, 0x44, 0x44, 0x43, 0x42, 0x41, 0x3f, 0x3f, 0x3f, 0x3e, 0x3d, 0x3c, 1958 0x3a, 0x3a, 0x3a, 0x39, 0x38, 0x37, 0x35, 0x35, 0x35, 0x34, 0x33, 0x32, 0x30, 0x30, 0x30, 0x2f, 1959 0x2e, 0x2d, 0x2b, 0x2b, 0x2b, 0x2a, 0x29, 0x28, 0x26, 0x26, 0x26, 0x25, 0x24, 0x23, 0x21, 0x21, 1960 0x21, 0x20, 0x1f, 0x1e, 0x1c, 0x1c, 0x1c, 0x1b, 0x1a, 0x19, 0x17, 0x17, 0x17, 0x16, 0x15, 0x14, 1961 0x12, 0x12, 0x12, 0x11, 0x10, 0xf, 0xd, 0xd, 0xd, 0xc, 0xb, 0xa, 0x8, 0x8, 0x8, 0x7, 0x6, 0x5, 1962 0x3, 0x3, 0x3, 0x2, 0x1, 0x0, 1963 } 1964 require.Equal(t, exp, results) 1965 } 1966 1967 func testManyParamsResultsSwapperListener(t *testing.T, r wazero.Runtime) { 1968 var buf bytes.Buffer 1969 ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf)) 1970 1971 bin, params := manyParamsResultsMod() 1972 mod, err := r.Instantiate(ctx, bin) 1973 require.NoError(t, err) 1974 1975 main := mod.ExportedFunction("swapper") 1976 require.NotNil(t, main) 1977 1978 results, err := main.Call(ctx, params...) 1979 require.NoError(t, err) 1980 1981 require.Equal(t, ` 1982 --> .swapper([0:i32]=0,[1:i64]=1,[2:f32]=3e-45,[3:f64]=1.5e-323,[4:v128]=00000000000000030000000000000003,[5:i32]=5,[6:i64]=6,[7:f32]=1e-44,[8:f64]=4e-323,[9:v128]=00000000000000080000000000000008,[10:i32]=10,[11:i64]=11,[12:f32]=1.7e-44,[13:f64]=6.4e-323,[14:v128]=000000000000000d000000000000000d,[15:i32]=15,[16:i64]=16,[17:f32]=2.4e-44,[18:f64]=9e-323,[19:v128]=00000000000000120000000000000012,[20:i32]=20,[21:i64]=21,[22:f32]=3.1e-44,[23:f64]=1.14e-322,[24:v128]=00000000000000170000000000000017,[25:i32]=25,[26:i64]=26,[27:f32]=3.8e-44,[28:f64]=1.4e-322,[29:v128]=000000000000001c000000000000001c,[30:i32]=30,[31:i64]=31,[32:f32]=4.5e-44,[33:f64]=1.63e-322,[34:v128]=00000000000000210000000000000021,[35:i32]=35,[36:i64]=36,[37:f32]=5.2e-44,[38:f64]=1.9e-322,[39:v128]=00000000000000260000000000000026,[40:i32]=40,[41:i64]=41,[42:f32]=5.9e-44,[43:f64]=2.1e-322,[44:v128]=000000000000002b000000000000002b,[45:i32]=45,[46:i64]=46,[47:f32]=6.6e-44,[48:f64]=2.37e-322,[49:v128]=00000000000000300000000000000030,[50:i32]=50,[51:i64]=51,[52:f32]=7.3e-44,[53:f64]=2.6e-322,[54:v128]=00000000000000350000000000000035,[55:i32]=55,[56:i64]=56,[57:f32]=8e-44,[58:f64]=2.87e-322,[59:v128]=000000000000003a000000000000003a,[60:i32]=60,[61:i64]=61,[62:f32]=8.7e-44,[63:f64]=3.1e-322,[64:v128]=000000000000003f000000000000003f,[65:i32]=65,[66:i64]=66,[67:f32]=9.4e-44,[68:f64]=3.36e-322,[69:v128]=00000000000000440000000000000044,[70:i32]=70,[71:i64]=71,[72:f32]=1.01e-43,[73:f64]=3.6e-322,[74:v128]=00000000000000490000000000000049,[75:i32]=75,[76:i64]=76,[77:f32]=1.08e-43,[78:f64]=3.85e-322,[79:v128]=000000000000004e000000000000004e,[80:i32]=80,[81:i64]=81,[82:f32]=1.15e-43,[83:f64]=4.1e-322,[84:v128]=00000000000000530000000000000053,[85:i32]=85,[86:i64]=86,[87:f32]=1.22e-43,[88:f64]=4.35e-322,[89:v128]=00000000000000580000000000000058,[90:i32]=90,[91:i64]=91,[92:f32]=1.29e-43,[93:f64]=4.6e-322,[94:v128]=000000000000005d000000000000005d,[95:i32]=95,[96:i64]=96,[97:f32]=1.36e-43,[98:f64]=4.84e-322,[99:v128]=00000000000000620000000000000062) 1983 <-- (00000000000000620000000000000062,4.84e-322,1.36e-43,96,95,000000000000005d000000000000005d,4.6e-322,1.29e-43,91,90,00000000000000580000000000000058,4.35e-322,1.22e-43,86,85,00000000000000530000000000000053,4.1e-322,1.15e-43,81,80,000000000000004e000000000000004e,3.85e-322,1.08e-43,76,75,00000000000000490000000000000049,3.6e-322,1.01e-43,71,70,00000000000000440000000000000044,3.36e-322,9.4e-44,66,65,000000000000003f000000000000003f,3.1e-322,8.7e-44,61,60,000000000000003a000000000000003a,2.87e-322,8e-44,56,55,00000000000000350000000000000035,2.6e-322,7.3e-44,51,50,00000000000000300000000000000030,2.37e-322,6.6e-44,46,45,000000000000002b000000000000002b,2.1e-322,5.9e-44,41,40,00000000000000260000000000000026,1.9e-322,5.2e-44,36,35,00000000000000210000000000000021,1.63e-322,4.5e-44,31,30,000000000000001c000000000000001c,1.4e-322,3.8e-44,26,25,00000000000000170000000000000017,1.14e-322,3.1e-44,21,20,00000000000000120000000000000012,9e-323,2.4e-44,16,15,000000000000000d000000000000000d,6.4e-323,1.7e-44,11,10,00000000000000080000000000000008,4e-323,1e-44,6,5,00000000000000030000000000000003,1.5e-323,3e-45,1,0) 1984 `, "\n"+buf.String()) 1985 1986 exp := []uint64{ 1987 0x62, 0x62, 0x62, 0x61, 0x60, 0x5f, 0x5d, 0x5d, 0x5d, 0x5c, 0x5b, 0x5a, 0x58, 0x58, 0x58, 0x57, 1988 0x56, 0x55, 0x53, 0x53, 0x53, 0x52, 0x51, 0x50, 0x4e, 0x4e, 0x4e, 0x4d, 0x4c, 0x4b, 0x49, 0x49, 1989 0x49, 0x48, 0x47, 0x46, 0x44, 0x44, 0x44, 0x43, 0x42, 0x41, 0x3f, 0x3f, 0x3f, 0x3e, 0x3d, 0x3c, 1990 0x3a, 0x3a, 0x3a, 0x39, 0x38, 0x37, 0x35, 0x35, 0x35, 0x34, 0x33, 0x32, 0x30, 0x30, 0x30, 0x2f, 1991 0x2e, 0x2d, 0x2b, 0x2b, 0x2b, 0x2a, 0x29, 0x28, 0x26, 0x26, 0x26, 0x25, 0x24, 0x23, 0x21, 0x21, 1992 0x21, 0x20, 0x1f, 0x1e, 0x1c, 0x1c, 0x1c, 0x1b, 0x1a, 0x19, 0x17, 0x17, 0x17, 0x16, 0x15, 0x14, 1993 0x12, 0x12, 0x12, 0x11, 0x10, 0xf, 0xd, 0xd, 0xd, 0xc, 0xb, 0xa, 0x8, 0x8, 0x8, 0x7, 0x6, 0x5, 1994 0x3, 0x3, 0x3, 0x2, 0x1, 0x0, 1995 } 1996 require.Equal(t, exp, results) 1997 } 1998 1999 func testManyParamsResultsMain(t *testing.T, r wazero.Runtime) { 2000 ctx := context.Background() 2001 2002 bin, params := manyParamsResultsMod() 2003 mod, err := r.Instantiate(ctx, bin) 2004 require.NoError(t, err) 2005 2006 main := mod.ExportedFunction("main") 2007 require.NotNil(t, main) 2008 2009 results, err := main.Call(ctx, params...) 2010 require.NoError(t, err) 2011 2012 exp := []uint64{ 2013 98, 98, 196, 194, 192, 190, 93, 93, 186, 184, 182, 180, 88, 88, 176, 174, 172, 170, 83, 83, 166, 164, 162, 2014 160, 78, 78, 156, 154, 152, 150, 73, 73, 146, 144, 142, 140, 68, 68, 136, 134, 132, 130, 63, 63, 126, 124, 2015 122, 120, 58, 58, 116, 114, 112, 110, 53, 53, 106, 104, 102, 100, 48, 48, 96, 94, 92, 90, 43, 43, 86, 84, 2016 82, 80, 38, 38, 76, 74, 72, 70, 33, 33, 66, 64, 62, 60, 28, 28, 56, 54, 52, 50, 23, 23, 46, 44, 42, 40, 18, 2017 18, 36, 34, 32, 30, 13, 13, 26, 24, 22, 20, 8, 8, 16, 14, 12, 10, 3, 3, 6, 4, 2, 0, 2018 } 2019 require.Equal(t, exp, results) 2020 } 2021 2022 func testManyParamsResultsMainListener(t *testing.T, r wazero.Runtime) { 2023 var buf bytes.Buffer 2024 ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf)) 2025 2026 bin, params := manyParamsResultsMod() 2027 mod, err := r.Instantiate(ctx, bin) 2028 require.NoError(t, err) 2029 2030 main := mod.ExportedFunction("main") 2031 require.NotNil(t, main) 2032 2033 results, err := main.Call(ctx, params...) 2034 require.NoError(t, err) 2035 2036 require.Equal(t, ` 2037 --> .main([0:i32]=0,[1:i64]=1,[2:f32]=3e-45,[3:f64]=1.5e-323,[4:v128]=00000000000000030000000000000003,[5:i32]=5,[6:i64]=6,[7:f32]=1e-44,[8:f64]=4e-323,[9:v128]=00000000000000080000000000000008,[10:i32]=10,[11:i64]=11,[12:f32]=1.7e-44,[13:f64]=6.4e-323,[14:v128]=000000000000000d000000000000000d,[15:i32]=15,[16:i64]=16,[17:f32]=2.4e-44,[18:f64]=9e-323,[19:v128]=00000000000000120000000000000012,[20:i32]=20,[21:i64]=21,[22:f32]=3.1e-44,[23:f64]=1.14e-322,[24:v128]=00000000000000170000000000000017,[25:i32]=25,[26:i64]=26,[27:f32]=3.8e-44,[28:f64]=1.4e-322,[29:v128]=000000000000001c000000000000001c,[30:i32]=30,[31:i64]=31,[32:f32]=4.5e-44,[33:f64]=1.63e-322,[34:v128]=00000000000000210000000000000021,[35:i32]=35,[36:i64]=36,[37:f32]=5.2e-44,[38:f64]=1.9e-322,[39:v128]=00000000000000260000000000000026,[40:i32]=40,[41:i64]=41,[42:f32]=5.9e-44,[43:f64]=2.1e-322,[44:v128]=000000000000002b000000000000002b,[45:i32]=45,[46:i64]=46,[47:f32]=6.6e-44,[48:f64]=2.37e-322,[49:v128]=00000000000000300000000000000030,[50:i32]=50,[51:i64]=51,[52:f32]=7.3e-44,[53:f64]=2.6e-322,[54:v128]=00000000000000350000000000000035,[55:i32]=55,[56:i64]=56,[57:f32]=8e-44,[58:f64]=2.87e-322,[59:v128]=000000000000003a000000000000003a,[60:i32]=60,[61:i64]=61,[62:f32]=8.7e-44,[63:f64]=3.1e-322,[64:v128]=000000000000003f000000000000003f,[65:i32]=65,[66:i64]=66,[67:f32]=9.4e-44,[68:f64]=3.36e-322,[69:v128]=00000000000000440000000000000044,[70:i32]=70,[71:i64]=71,[72:f32]=1.01e-43,[73:f64]=3.6e-322,[74:v128]=00000000000000490000000000000049,[75:i32]=75,[76:i64]=76,[77:f32]=1.08e-43,[78:f64]=3.85e-322,[79:v128]=000000000000004e000000000000004e,[80:i32]=80,[81:i64]=81,[82:f32]=1.15e-43,[83:f64]=4.1e-322,[84:v128]=00000000000000530000000000000053,[85:i32]=85,[86:i64]=86,[87:f32]=1.22e-43,[88:f64]=4.35e-322,[89:v128]=00000000000000580000000000000058,[90:i32]=90,[91:i64]=91,[92:f32]=1.29e-43,[93:f64]=4.6e-322,[94:v128]=000000000000005d000000000000005d,[95:i32]=95,[96:i64]=96,[97:f32]=1.36e-43,[98:f64]=4.84e-322,[99:v128]=00000000000000620000000000000062) 2038 --> .swapper([0:i32]=0,[1:i64]=1,[2:f32]=3e-45,[3:f64]=1.5e-323,[4:v128]=00000000000000030000000000000003,[5:i32]=5,[6:i64]=6,[7:f32]=1e-44,[8:f64]=4e-323,[9:v128]=00000000000000080000000000000008,[10:i32]=10,[11:i64]=11,[12:f32]=1.7e-44,[13:f64]=6.4e-323,[14:v128]=000000000000000d000000000000000d,[15:i32]=15,[16:i64]=16,[17:f32]=2.4e-44,[18:f64]=9e-323,[19:v128]=00000000000000120000000000000012,[20:i32]=20,[21:i64]=21,[22:f32]=3.1e-44,[23:f64]=1.14e-322,[24:v128]=00000000000000170000000000000017,[25:i32]=25,[26:i64]=26,[27:f32]=3.8e-44,[28:f64]=1.4e-322,[29:v128]=000000000000001c000000000000001c,[30:i32]=30,[31:i64]=31,[32:f32]=4.5e-44,[33:f64]=1.63e-322,[34:v128]=00000000000000210000000000000021,[35:i32]=35,[36:i64]=36,[37:f32]=5.2e-44,[38:f64]=1.9e-322,[39:v128]=00000000000000260000000000000026,[40:i32]=40,[41:i64]=41,[42:f32]=5.9e-44,[43:f64]=2.1e-322,[44:v128]=000000000000002b000000000000002b,[45:i32]=45,[46:i64]=46,[47:f32]=6.6e-44,[48:f64]=2.37e-322,[49:v128]=00000000000000300000000000000030,[50:i32]=50,[51:i64]=51,[52:f32]=7.3e-44,[53:f64]=2.6e-322,[54:v128]=00000000000000350000000000000035,[55:i32]=55,[56:i64]=56,[57:f32]=8e-44,[58:f64]=2.87e-322,[59:v128]=000000000000003a000000000000003a,[60:i32]=60,[61:i64]=61,[62:f32]=8.7e-44,[63:f64]=3.1e-322,[64:v128]=000000000000003f000000000000003f,[65:i32]=65,[66:i64]=66,[67:f32]=9.4e-44,[68:f64]=3.36e-322,[69:v128]=00000000000000440000000000000044,[70:i32]=70,[71:i64]=71,[72:f32]=1.01e-43,[73:f64]=3.6e-322,[74:v128]=00000000000000490000000000000049,[75:i32]=75,[76:i64]=76,[77:f32]=1.08e-43,[78:f64]=3.85e-322,[79:v128]=000000000000004e000000000000004e,[80:i32]=80,[81:i64]=81,[82:f32]=1.15e-43,[83:f64]=4.1e-322,[84:v128]=00000000000000530000000000000053,[85:i32]=85,[86:i64]=86,[87:f32]=1.22e-43,[88:f64]=4.35e-322,[89:v128]=00000000000000580000000000000058,[90:i32]=90,[91:i64]=91,[92:f32]=1.29e-43,[93:f64]=4.6e-322,[94:v128]=000000000000005d000000000000005d,[95:i32]=95,[96:i64]=96,[97:f32]=1.36e-43,[98:f64]=4.84e-322,[99:v128]=00000000000000620000000000000062) 2039 <-- (00000000000000620000000000000062,4.84e-322,1.36e-43,96,95,000000000000005d000000000000005d,4.6e-322,1.29e-43,91,90,00000000000000580000000000000058,4.35e-322,1.22e-43,86,85,00000000000000530000000000000053,4.1e-322,1.15e-43,81,80,000000000000004e000000000000004e,3.85e-322,1.08e-43,76,75,00000000000000490000000000000049,3.6e-322,1.01e-43,71,70,00000000000000440000000000000044,3.36e-322,9.4e-44,66,65,000000000000003f000000000000003f,3.1e-322,8.7e-44,61,60,000000000000003a000000000000003a,2.87e-322,8e-44,56,55,00000000000000350000000000000035,2.6e-322,7.3e-44,51,50,00000000000000300000000000000030,2.37e-322,6.6e-44,46,45,000000000000002b000000000000002b,2.1e-322,5.9e-44,41,40,00000000000000260000000000000026,1.9e-322,5.2e-44,36,35,00000000000000210000000000000021,1.63e-322,4.5e-44,31,30,000000000000001c000000000000001c,1.4e-322,3.8e-44,26,25,00000000000000170000000000000017,1.14e-322,3.1e-44,21,20,00000000000000120000000000000012,9e-323,2.4e-44,16,15,000000000000000d000000000000000d,6.4e-323,1.7e-44,11,10,00000000000000080000000000000008,4e-323,1e-44,6,5,00000000000000030000000000000003,1.5e-323,3e-45,1,0) 2040 --> .doubler([0:v128]=00000000000000620000000000000062,[1:f64]=4.84e-322,[2:f32]=1.36e-43,[3:i64]=96,[4:i32]=95,[5:v128]=000000000000005d000000000000005d,[6:f64]=4.6e-322,[7:f32]=1.29e-43,[8:i64]=91,[9:i32]=90,[10:v128]=00000000000000580000000000000058,[11:f64]=4.35e-322,[12:f32]=1.22e-43,[13:i64]=86,[14:i32]=85,[15:v128]=00000000000000530000000000000053,[16:f64]=4.1e-322,[17:f32]=1.15e-43,[18:i64]=81,[19:i32]=80,[20:v128]=000000000000004e000000000000004e,[21:f64]=3.85e-322,[22:f32]=1.08e-43,[23:i64]=76,[24:i32]=75,[25:v128]=00000000000000490000000000000049,[26:f64]=3.6e-322,[27:f32]=1.01e-43,[28:i64]=71,[29:i32]=70,[30:v128]=00000000000000440000000000000044,[31:f64]=3.36e-322,[32:f32]=9.4e-44,[33:i64]=66,[34:i32]=65,[35:v128]=000000000000003f000000000000003f,[36:f64]=3.1e-322,[37:f32]=8.7e-44,[38:i64]=61,[39:i32]=60,[40:v128]=000000000000003a000000000000003a,[41:f64]=2.87e-322,[42:f32]=8e-44,[43:i64]=56,[44:i32]=55,[45:v128]=00000000000000350000000000000035,[46:f64]=2.6e-322,[47:f32]=7.3e-44,[48:i64]=51,[49:i32]=50,[50:v128]=00000000000000300000000000000030,[51:f64]=2.37e-322,[52:f32]=6.6e-44,[53:i64]=46,[54:i32]=45,[55:v128]=000000000000002b000000000000002b,[56:f64]=2.1e-322,[57:f32]=5.9e-44,[58:i64]=41,[59:i32]=40,[60:v128]=00000000000000260000000000000026,[61:f64]=1.9e-322,[62:f32]=5.2e-44,[63:i64]=36,[64:i32]=35,[65:v128]=00000000000000210000000000000021,[66:f64]=1.63e-322,[67:f32]=4.5e-44,[68:i64]=31,[69:i32]=30,[70:v128]=000000000000001c000000000000001c,[71:f64]=1.4e-322,[72:f32]=3.8e-44,[73:i64]=26,[74:i32]=25,[75:v128]=00000000000000170000000000000017,[76:f64]=1.14e-322,[77:f32]=3.1e-44,[78:i64]=21,[79:i32]=20,[80:v128]=00000000000000120000000000000012,[81:f64]=9e-323,[82:f32]=2.4e-44,[83:i64]=16,[84:i32]=15,[85:v128]=000000000000000d000000000000000d,[86:f64]=6.4e-323,[87:f32]=1.7e-44,[88:i64]=11,[89:i32]=10,[90:v128]=00000000000000080000000000000008,[91:f64]=4e-323,[92:f32]=1e-44,[93:i64]=6,[94:i32]=5,[95:v128]=00000000000000030000000000000003,[96:f64]=1.5e-323,[97:f32]=3e-45,[98:i64]=1,[99:i32]=0) 2041 <-- (00000000000000620000000000000062,9.7e-322,2.72e-43,192,190,000000000000005d000000000000005d,9.2e-322,2.58e-43,182,180,00000000000000580000000000000058,8.7e-322,2.44e-43,172,170,00000000000000530000000000000053,8.2e-322,2.3e-43,162,160,000000000000004e000000000000004e,7.7e-322,2.16e-43,152,150,00000000000000490000000000000049,7.2e-322,2.02e-43,142,140,00000000000000440000000000000044,6.7e-322,1.88e-43,132,130,000000000000003f000000000000003f,6.23e-322,1.74e-43,122,120,000000000000003a000000000000003a,5.73e-322,1.6e-43,112,110,00000000000000350000000000000035,5.24e-322,1.46e-43,102,100,00000000000000300000000000000030,4.74e-322,1.32e-43,92,90,000000000000002b000000000000002b,4.25e-322,1.18e-43,82,80,00000000000000260000000000000026,3.75e-322,1.04e-43,72,70,00000000000000210000000000000021,3.26e-322,9e-44,62,60,000000000000001c000000000000001c,2.77e-322,7.6e-44,52,50,00000000000000170000000000000017,2.27e-322,6.2e-44,42,40,00000000000000120000000000000012,1.8e-322,4.8e-44,32,30,000000000000000d000000000000000d,1.3e-322,3.4e-44,22,20,00000000000000080000000000000008,8e-323,2e-44,12,10,00000000000000030000000000000003,3e-323,6e-45,2,0) 2042 <-- (00000000000000620000000000000062,9.7e-322,2.72e-43,192,190,000000000000005d000000000000005d,9.2e-322,2.58e-43,182,180,00000000000000580000000000000058,8.7e-322,2.44e-43,172,170,00000000000000530000000000000053,8.2e-322,2.3e-43,162,160,000000000000004e000000000000004e,7.7e-322,2.16e-43,152,150,00000000000000490000000000000049,7.2e-322,2.02e-43,142,140,00000000000000440000000000000044,6.7e-322,1.88e-43,132,130,000000000000003f000000000000003f,6.23e-322,1.74e-43,122,120,000000000000003a000000000000003a,5.73e-322,1.6e-43,112,110,00000000000000350000000000000035,5.24e-322,1.46e-43,102,100,00000000000000300000000000000030,4.74e-322,1.32e-43,92,90,000000000000002b000000000000002b,4.25e-322,1.18e-43,82,80,00000000000000260000000000000026,3.75e-322,1.04e-43,72,70,00000000000000210000000000000021,3.26e-322,9e-44,62,60,000000000000001c000000000000001c,2.77e-322,7.6e-44,52,50,00000000000000170000000000000017,2.27e-322,6.2e-44,42,40,00000000000000120000000000000012,1.8e-322,4.8e-44,32,30,000000000000000d000000000000000d,1.3e-322,3.4e-44,22,20,00000000000000080000000000000008,8e-323,2e-44,12,10,00000000000000030000000000000003,3e-323,6e-45,2,0) 2043 `, "\n"+buf.String()) 2044 2045 exp := []uint64{ 2046 98, 98, 196, 194, 192, 190, 93, 93, 186, 184, 182, 180, 88, 88, 176, 174, 172, 170, 83, 83, 166, 164, 162, 2047 160, 78, 78, 156, 154, 152, 150, 73, 73, 146, 144, 142, 140, 68, 68, 136, 134, 132, 130, 63, 63, 126, 124, 2048 122, 120, 58, 58, 116, 114, 112, 110, 53, 53, 106, 104, 102, 100, 48, 48, 96, 94, 92, 90, 43, 43, 86, 84, 2049 82, 80, 38, 38, 76, 74, 72, 70, 33, 33, 66, 64, 62, 60, 28, 28, 56, 54, 52, 50, 23, 23, 46, 44, 42, 40, 18, 2050 18, 36, 34, 32, 30, 13, 13, 26, 24, 22, 20, 8, 8, 16, 14, 12, 10, 3, 3, 6, 4, 2, 0, 2051 } 2052 require.Equal(t, exp, results) 2053 } 2054 2055 func testManyParamsResultsCallManyConstsAndPickLastVector(t *testing.T, r wazero.Runtime) { 2056 ctx := context.Background() 2057 2058 bin, _ := manyParamsResultsMod() 2059 mod, err := r.Instantiate(ctx, bin) 2060 require.NoError(t, err) 2061 2062 main := mod.ExportedFunction("call_many_consts_and_pick_last_vector") 2063 require.NotNil(t, main) 2064 2065 results, err := main.Call(ctx) 2066 require.NoError(t, err) 2067 exp := []uint64{0x5f5f5f5f5f5f5f5f, 0x5f5f5f5f5f5f5f5f} 2068 require.Equal(t, exp, results) 2069 } 2070 2071 func testManyParamsResultsCallManyConstsAndPickLastVectorListener(t *testing.T, r wazero.Runtime) { 2072 var buf bytes.Buffer 2073 ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf)) 2074 2075 bin, _ := manyParamsResultsMod() 2076 mod, err := r.Instantiate(ctx, bin) 2077 require.NoError(t, err) 2078 2079 main := mod.ExportedFunction("call_many_consts_and_pick_last_vector") 2080 require.NotNil(t, main) 2081 2082 results, err := main.Call(ctx) 2083 require.NoError(t, err) 2084 exp := []uint64{0x5f5f5f5f5f5f5f5f, 0x5f5f5f5f5f5f5f5f} 2085 require.Equal(t, exp, results) 2086 2087 require.Equal(t, ` 2088 --> .call_many_consts_and_pick_last_vector() 2089 --> .many_consts() 2090 <-- (0,0,0,0,00000000000000000000000000000000,5,5,6.254552e-36,1.766927440712025e-284,05050505050505050505050505050505,10,10,6.6463464e-33,2.6461938652294957e-260,0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a,15,15,7.0533445e-30,3.815736827118017e-236,0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f,20,20,7.47605e-27,5.964208835435795e-212,14141414141414141414141414141414,25,25,7.914983e-24,9.01285756841504e-188,19191919191919191919191919191919,30,30,8.3706784e-21,1.3075051467559279e-163,1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e,35,35,8.843688e-18,2.008776679223492e-139,23232323232323232323232323232323,40,40,9.334581e-15,3.0654356309538037e-115,28282828282828282828282828282828,45,45,9.8439425e-12,4.4759381595361623e-91,2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d,50,50,1.0372377e-08,6.749300603603778e-67,32323232323232323232323232323232,55,55,1.0920506e-05,1.0410273767909543e-42,37373737373737373737373737373737,60,60,0.01148897,1.5306383611560062e-18,3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c,65,65,12.078431,2.2616345098039214e+06,41414141414141414141414141414141,70,70,12689.568,3.5295369653413445e+30,46464646464646464646464646464646,75,75,1.3323083e+07,5.2285141982483265e+54,4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b,80,80,1.3979697e+10,7.556001431015456e+78,50505050505050505050505050505050,85,85,1.4660155e+13,1.1945305291614955e+103,55555555555555555555555555555555,90,90,1.5365222e+16,1.7838867517321418e+127,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a,95,95,1.6095688e+19,2.5673651826636406e+151,5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f) 2091 --> .pick_last_vector([0:i32]=0,[1:i64]=0,[2:f32]=0,[3:f64]=0,[4:v128]=00000000000000000000000000000000,[5:i32]=5,[6:i64]=5,[7:f32]=6.254552e-36,[8:f64]=1.766927440712025e-284,[9:v128]=05050505050505050505050505050505,[10:i32]=10,[11:i64]=10,[12:f32]=6.6463464e-33,[13:f64]=2.6461938652294957e-260,[14:v128]=0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a,[15:i32]=15,[16:i64]=15,[17:f32]=7.0533445e-30,[18:f64]=3.815736827118017e-236,[19:v128]=0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f,[20:i32]=20,[21:i64]=20,[22:f32]=7.47605e-27,[23:f64]=5.964208835435795e-212,[24:v128]=14141414141414141414141414141414,[25:i32]=25,[26:i64]=25,[27:f32]=7.914983e-24,[28:f64]=9.01285756841504e-188,[29:v128]=19191919191919191919191919191919,[30:i32]=30,[31:i64]=30,[32:f32]=8.3706784e-21,[33:f64]=1.3075051467559279e-163,[34:v128]=1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e,[35:i32]=35,[36:i64]=35,[37:f32]=8.843688e-18,[38:f64]=2.008776679223492e-139,[39:v128]=23232323232323232323232323232323,[40:i32]=40,[41:i64]=40,[42:f32]=9.334581e-15,[43:f64]=3.0654356309538037e-115,[44:v128]=28282828282828282828282828282828,[45:i32]=45,[46:i64]=45,[47:f32]=9.8439425e-12,[48:f64]=4.4759381595361623e-91,[49:v128]=2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d,[50:i32]=50,[51:i64]=50,[52:f32]=1.0372377e-08,[53:f64]=6.749300603603778e-67,[54:v128]=32323232323232323232323232323232,[55:i32]=55,[56:i64]=55,[57:f32]=1.0920506e-05,[58:f64]=1.0410273767909543e-42,[59:v128]=37373737373737373737373737373737,[60:i32]=60,[61:i64]=60,[62:f32]=0.01148897,[63:f64]=1.5306383611560062e-18,[64:v128]=3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c,[65:i32]=65,[66:i64]=65,[67:f32]=12.078431,[68:f64]=2.2616345098039214e+06,[69:v128]=41414141414141414141414141414141,[70:i32]=70,[71:i64]=70,[72:f32]=12689.568,[73:f64]=3.5295369653413445e+30,[74:v128]=46464646464646464646464646464646,[75:i32]=75,[76:i64]=75,[77:f32]=1.3323083e+07,[78:f64]=5.2285141982483265e+54,[79:v128]=4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b,[80:i32]=80,[81:i64]=80,[82:f32]=1.3979697e+10,[83:f64]=7.556001431015456e+78,[84:v128]=50505050505050505050505050505050,[85:i32]=85,[86:i64]=85,[87:f32]=1.4660155e+13,[88:f64]=1.1945305291614955e+103,[89:v128]=55555555555555555555555555555555,[90:i32]=90,[91:i64]=90,[92:f32]=1.5365222e+16,[93:f64]=1.7838867517321418e+127,[94:v128]=5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a,[95:i32]=95,[96:i64]=95,[97:f32]=1.6095688e+19,[98:f64]=2.5673651826636406e+151,[99:v128]=5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f) 2092 <-- 5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f 2093 <-- 5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f 2094 `, "\n"+buf.String()) 2095 } 2096 2097 func testImportedMutableGlobalUpdate(t *testing.T, r wazero.Runtime) { 2098 importedBin := binaryencoding.EncodeModule(&wasm.Module{ 2099 ExportSection: []wasm.Export{ 2100 {Name: "g", Type: wasm.ExternTypeGlobal, Index: 0}, 2101 }, 2102 GlobalSection: []wasm.Global{ 2103 { 2104 Type: wasm.GlobalType{ValType: i32, Mutable: true}, 2105 Init: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{1}}, 2106 }, 2107 }, 2108 NameSection: &wasm.NameSection{ModuleName: "imported"}, 2109 }) 2110 2111 mainBin := binaryencoding.EncodeModule(&wasm.Module{ 2112 ImportSection: []wasm.Import{{ 2113 Type: wasm.ExternTypeGlobal, 2114 Module: "imported", 2115 Name: "g", 2116 DescGlobal: wasm.GlobalType{ValType: i32, Mutable: true}, 2117 }}, 2118 TypeSection: []wasm.FunctionType{{Results: []wasm.ValueType{i32}}}, 2119 ExportSection: []wasm.Export{ 2120 {Name: "", Type: wasm.ExternTypeFunc, Index: 0}, 2121 {Name: "g", Type: wasm.ExternTypeGlobal, Index: 0}, 2122 }, 2123 FunctionSection: []wasm.Index{0}, 2124 CodeSection: []wasm.Code{ 2125 {Body: []byte{ 2126 wasm.OpcodeGlobalGet, 0, 2127 wasm.OpcodeI32Const, 2, 2128 wasm.OpcodeGlobalSet, 0, 2129 wasm.OpcodeEnd, 2130 }}, 2131 }, 2132 }) 2133 2134 ctx := context.Background() 2135 importedMod, err := r.Instantiate(ctx, importedBin) 2136 require.NoError(t, err) 2137 2138 mainMod, err := r.Instantiate(ctx, mainBin) 2139 require.NoError(t, err) 2140 2141 main := mainMod.ExportedFunction("") 2142 require.NotNil(t, main) 2143 2144 res, err := main.Call(ctx) 2145 require.NoError(t, err) 2146 2147 prevValue := res[0] 2148 require.Equal(t, uint64(1), prevValue) 2149 2150 g := importedMod.ExportedGlobal("g") 2151 require.NotNil(t, g) 2152 2153 v := g.Get() 2154 require.Equal(t, uint64(2), v) 2155 2156 reExportedG := mainMod.ExportedGlobal("g") 2157 v = reExportedG.Get() 2158 require.Equal(t, uint64(2), v) 2159 } 2160 2161 func testHugeCallStackUnwind(t *testing.T, r wazero.Runtime) { 2162 ctx := context.Background() 2163 _, err := r.Instantiate(ctx, hugeCallStackUnwind) 2164 require.Error(t, err) 2165 require.Equal(t, `start function[0] failed: wasm error: integer divide by zero 2166 wasm stack trace: 2167 .$0() 2168 .$0() 2169 .$0() 2170 .$0() 2171 .$0() 2172 .$0() 2173 .$0() 2174 .$0() 2175 .$0() 2176 .$0() 2177 .$0() 2178 .$0() 2179 .$0() 2180 .$0() 2181 .$0() 2182 .$0() 2183 .$0() 2184 .$0() 2185 .$0() 2186 .$0() 2187 .$0() 2188 .$0() 2189 .$0() 2190 .$0() 2191 .$0() 2192 .$0() 2193 .$0() 2194 .$0() 2195 .$0() 2196 .$0() 2197 ... maybe followed by omitted frames`, err.Error()) 2198 } 2199 2200 // testCloseTableExportingModule tests the situation where the module instance that 2201 // is the initial owner of the table is closed and then try to call call_indirect on the table. 2202 // 2203 // This is in practice extremely edge case and shouldn't occur in real world, but in any way, seg fault should not occur. 2204 func testCloseTableExportingModule(t *testing.T, r wazero.Runtime) { 2205 exportingBin := binaryencoding.EncodeModule(&wasm.Module{ 2206 ExportSection: []wasm.Export{ 2207 {Name: "t", Type: wasm.ExternTypeTable, Index: 0}, 2208 }, 2209 TableSection: []wasm.Table{{Type: wasm.RefTypeFuncref, Min: 10}}, 2210 NameSection: &wasm.NameSection{ModuleName: "exporting"}, 2211 ElementSection: []wasm.ElementSegment{ 2212 { 2213 OffsetExpr: wasm.ConstantExpression{ 2214 Opcode: wasm.OpcodeI32Const, 2215 Data: leb128.EncodeInt32(5), 2216 }, TableIndex: 0, Type: wasm.RefTypeFuncref, Mode: wasm.ElementModeActive, 2217 // Set the function 0, 1 at table offset 5. 2218 Init: []wasm.Index{0, 1}, 2219 }, 2220 }, 2221 TypeSection: []wasm.FunctionType{{Results: []wasm.ValueType{i32}}}, 2222 FunctionSection: []wasm.Index{0, 0}, 2223 CodeSection: []wasm.Code{ 2224 {Body: []byte{wasm.OpcodeI32Const, 1, wasm.OpcodeEnd}}, 2225 {Body: []byte{wasm.OpcodeI32Const, 2, wasm.OpcodeEnd}}, 2226 }, 2227 }) 2228 2229 mainBin := binaryencoding.EncodeModule(&wasm.Module{ 2230 ImportSection: []wasm.Import{{ 2231 Type: wasm.ExternTypeTable, 2232 Module: "exporting", 2233 Name: "t", 2234 DescTable: wasm.Table{Type: wasm.RefTypeFuncref, Min: 10}, 2235 }}, 2236 TypeSection: []wasm.FunctionType{ 2237 {Results: []wasm.ValueType{i32}}, // Type for functions in the table. 2238 {Params: []wasm.ValueType{i32}, Results: []wasm.ValueType{i32}}, // Type for the main function. 2239 }, 2240 ExportSection: []wasm.Export{ 2241 {Name: "", Type: wasm.ExternTypeFunc, Index: 0}, 2242 }, 2243 FunctionSection: []wasm.Index{1}, 2244 CodeSection: []wasm.Code{ 2245 {Body: []byte{ 2246 wasm.OpcodeLocalGet, 0, 2247 wasm.OpcodeCallIndirect, 0, 0, 2248 wasm.OpcodeEnd, 2249 }}, 2250 }, 2251 }) 2252 2253 ctx := context.Background() 2254 exportingMod, err := r.Instantiate(ctx, exportingBin) 2255 require.NoError(t, err) 2256 2257 mainMod, err := r.Instantiate(ctx, mainBin) 2258 require.NoError(t, err) 2259 2260 main := mainMod.ExportedFunction("") 2261 require.NotNil(t, main) 2262 2263 err = exportingMod.Close(ctx) 2264 require.NoError(t, err) 2265 2266 // Trigger GC to make sure the module instance that is the initial owner of the table is collected. 2267 runtime.GC() 2268 2269 // Call call_indirect multiple times, should be safe. 2270 for i := 0; i < 10; i++ { 2271 _, err = main.Call(ctx, 0) 2272 // Null function call 2273 require.Error(t, err) 2274 2275 res, err := main.Call(ctx, 5) 2276 require.NoError(t, err) 2277 require.Equal(t, uint64(1), res[0]) 2278 2279 res, err = main.Call(ctx, 6) 2280 require.NoError(t, err) 2281 require.Equal(t, uint64(2), res[0]) 2282 time.Sleep(time.Millisecond * 10) 2283 } 2284 } 2285 2286 // testCloseTableImportingModule is similar to testCloseTableExportingModule, but the module 2287 // importing the table sets the function reference in the table, and then the module instance 2288 // that is the initial owner of the table will call call_indirect on the table. 2289 // 2290 // This is in practice extremely edge case and shouldn't occur in real world, but in any way, seg fault should not occur. 2291 func testCloseTableImportingModule(t *testing.T, r wazero.Runtime) { 2292 exportingBin := binaryencoding.EncodeModule(&wasm.Module{ 2293 ExportSection: []wasm.Export{ 2294 {Name: "t", Type: wasm.ExternTypeTable, Index: 0}, 2295 {Name: "main", Type: wasm.ExternTypeFunc, Index: 0}, 2296 }, 2297 TableSection: []wasm.Table{{Type: wasm.RefTypeFuncref, Min: 10}}, 2298 NameSection: &wasm.NameSection{ModuleName: "exporting"}, 2299 TypeSection: []wasm.FunctionType{ 2300 {Results: []wasm.ValueType{i32}}, // Type for functions in the table. 2301 {Params: []wasm.ValueType{i32}, Results: []wasm.ValueType{i32}}, // Type for the main function. 2302 }, 2303 FunctionSection: []wasm.Index{1}, 2304 CodeSection: []wasm.Code{ 2305 {Body: []byte{ 2306 wasm.OpcodeLocalGet, 0, 2307 wasm.OpcodeCallIndirect, 0, 0, 2308 wasm.OpcodeEnd, 2309 }}, 2310 }, 2311 }) 2312 2313 importingBin := binaryencoding.EncodeModule(&wasm.Module{ 2314 NameSection: &wasm.NameSection{ModuleName: "importing"}, 2315 ImportSection: []wasm.Import{{ 2316 Type: wasm.ExternTypeTable, 2317 Module: "exporting", 2318 Name: "t", 2319 DescTable: wasm.Table{Type: wasm.RefTypeFuncref, Min: 10}, 2320 }}, 2321 TypeSection: []wasm.FunctionType{ 2322 {Results: []wasm.ValueType{i32}}, // Type for functions in the table. 2323 }, 2324 ElementSection: []wasm.ElementSegment{ 2325 { 2326 OffsetExpr: wasm.ConstantExpression{ 2327 Opcode: wasm.OpcodeI32Const, 2328 Data: leb128.EncodeInt32(5), 2329 }, TableIndex: 0, Type: wasm.RefTypeFuncref, Mode: wasm.ElementModeActive, 2330 // Set the function 0, 1 at table offset 5. 2331 Init: []wasm.Index{0, 1}, 2332 }, 2333 }, 2334 FunctionSection: []wasm.Index{0, 0}, 2335 CodeSection: []wasm.Code{ 2336 {Body: []byte{wasm.OpcodeI32Const, 1, wasm.OpcodeEnd}}, 2337 {Body: []byte{wasm.OpcodeI32Const, 2, wasm.OpcodeEnd}}, 2338 }, 2339 }) 2340 2341 ctx := context.Background() 2342 exportingMod, err := r.Instantiate(ctx, exportingBin) 2343 require.NoError(t, err) 2344 2345 instantiateClose(t, r, ctx, importingBin) 2346 2347 // Trigger GC to make sure the module instance that is the initial owner of the references in the table is collected. 2348 time.Sleep(time.Millisecond * 10) 2349 runtime.GC() 2350 time.Sleep(time.Millisecond * 10) 2351 2352 main := exportingMod.ExportedFunction("main") 2353 require.NotNil(t, main) 2354 2355 // Call call_indirect multiple times, should be safe. 2356 for i := 0; i < 10; i++ { 2357 _, err = main.Call(ctx, 0) 2358 // Null function call 2359 require.Error(t, err) 2360 2361 res, err := main.Call(ctx, 5) 2362 require.NoError(t, err) 2363 require.Equal(t, uint64(1), res[0]) 2364 2365 res, err = main.Call(ctx, 6) 2366 require.NoError(t, err) 2367 require.Equal(t, uint64(2), res[0]) 2368 runtime.GC() 2369 time.Sleep(time.Millisecond * 10) 2370 } 2371 } 2372 2373 func instantiateClose(t *testing.T, r wazero.Runtime, ctx context.Context, bin []byte) { 2374 compiled, err := r.CompileModule(ctx, bin) 2375 require.NoError(t, err) 2376 m, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig()) 2377 require.NoError(t, err) 2378 err = m.Close(ctx) 2379 require.NoError(t, err) 2380 require.NoError(t, compiled.Close(ctx)) 2381 runtime.GC() 2382 } 2383 2384 // testHugeBinary is supposed to do e2e on huge relocation in arm64. 2385 func testHugeBinary(t *testing.T, r wazero.Runtime) { 2386 if testing.Short() { 2387 t.Skip("skipping testHugeBinary in short mode since it takes a long time") 2388 } 2389 var addFuncBody []byte 2390 for i := 0; i < 1024; i++ { 2391 addFuncBody = append(addFuncBody, wasm.OpcodeLocalGet, 0) 2392 addFuncBody = append(addFuncBody, wasm.OpcodeI32Const) 2393 addFuncBody = append(addFuncBody, leb128.EncodeInt32(int32(i))...) 2394 addFuncBody = append(addFuncBody, wasm.OpcodeI32Add) 2395 addFuncBody = append(addFuncBody, wasm.OpcodeLocalSet, 0) 2396 } 2397 addFuncBody = append(addFuncBody, wasm.OpcodeLocalGet, 0, wasm.OpcodeEnd) 2398 2399 const functionCount = 40000 2400 // The first function calls the second last function. 2401 var firstFunctionBody []byte 2402 firstFunctionBody = append(firstFunctionBody, wasm.OpcodeLocalGet, 0) 2403 firstFunctionBody = append(firstFunctionBody, wasm.OpcodeCall) 2404 firstFunctionBody = append(firstFunctionBody, leb128.EncodeUint32(functionCount-2)...) 2405 firstFunctionBody = append(firstFunctionBody, wasm.OpcodeEnd) 2406 // The last function calls the second function. 2407 lastFunctionBody := []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeCall, 1, wasm.OpcodeEnd} 2408 2409 // Create a Code section with 20000 functions. 2410 codes := make([]wasm.Code, functionCount) 2411 codes[0] = wasm.Code{Body: firstFunctionBody} 2412 for i := 0; i < functionCount-2; i++ { 2413 codes[i+1] = wasm.Code{Body: addFuncBody} 2414 } 2415 codes[functionCount-1] = wasm.Code{Body: lastFunctionBody} 2416 2417 funcType := wasm.FunctionType{Params: []wasm.ValueType{i32}, Results: []wasm.ValueType{i32}} 2418 m := &wasm.Module{} 2419 m.FunctionSection = make([]wasm.Index, functionCount) 2420 m.CodeSection = codes 2421 m.TypeSection = []wasm.FunctionType{funcType} 2422 // Export the first and last function. 2423 m.ExportSection = []wasm.Export{ 2424 {Name: "first", Type: wasm.ExternTypeFunc, Index: 0}, 2425 {Name: "last", Type: wasm.ExternTypeFunc, Index: functionCount - 1}, 2426 } 2427 2428 bin := binaryencoding.EncodeModule(m) 2429 2430 ctx := context.Background() 2431 mod, err := r.Instantiate(ctx, bin) 2432 require.NoError(t, err) 2433 2434 first := mod.ExportedFunction("first") 2435 require.NotNil(t, first) 2436 res, err := first.Call(ctx, 0) 2437 require.NoError(t, err) 2438 require.Equal(t, uint64(1023*1024/2), res[0]) 2439 2440 last := mod.ExportedFunction("last") 2441 require.NotNil(t, last) 2442 res, err = last.Call(ctx, 0) 2443 require.NoError(t, err) 2444 require.Equal(t, uint64(1023*1024/2), res[0]) 2445 }