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