github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/engine/wazevo/e2e_test.go (about)

     1  package wazevo_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/binary"
     7  	"fmt"
     8  	"math"
     9  	"testing"
    10  
    11  	"github.com/tetratelabs/wazero"
    12  	"github.com/tetratelabs/wazero/api"
    13  	"github.com/tetratelabs/wazero/experimental"
    14  	"github.com/tetratelabs/wazero/experimental/logging"
    15  	"github.com/tetratelabs/wazero/internal/engine/wazevo/testcases"
    16  	"github.com/tetratelabs/wazero/internal/leb128"
    17  	"github.com/tetratelabs/wazero/internal/testing/binaryencoding"
    18  	"github.com/tetratelabs/wazero/internal/testing/dwarftestdata"
    19  	"github.com/tetratelabs/wazero/internal/testing/require"
    20  	"github.com/tetratelabs/wazero/internal/wasm"
    21  )
    22  
    23  const (
    24  	i32  = wasm.ValueTypeI32
    25  	i64  = wasm.ValueTypeI64
    26  	f32  = wasm.ValueTypeF32
    27  	f64  = wasm.ValueTypeF64
    28  	v128 = wasm.ValueTypeV128
    29  )
    30  
    31  func TestE2E(t *testing.T) {
    32  	tmp := t.TempDir()
    33  	type callCase struct {
    34  		funcName           string // defaults to testcases.ExportedFunctionName
    35  		params, expResults []uint64
    36  		expErr             string
    37  	}
    38  	for _, tc := range []struct {
    39  		name        string
    40  		imported, m *wasm.Module
    41  		calls       []callCase
    42  		features    api.CoreFeatures
    43  		setupMemory func(mem api.Memory)
    44  	}{
    45  		{
    46  			name: "empty", m: testcases.Empty.Module,
    47  			calls: []callCase{{expResults: []uint64{}}},
    48  		},
    49  		{
    50  			name: "only_return", m: testcases.OnlyReturn.Module,
    51  			calls: []callCase{{expResults: []uint64{}}},
    52  		},
    53  		{
    54  			name: "selects", m: testcases.Selects.Module,
    55  			calls: []callCase{
    56  				{
    57  					params: []uint64{
    58  						0, 1, // i32,
    59  						200, 100, // i64,
    60  						uint64(math.Float32bits(3.0)), uint64(math.Float32bits(10.0)),
    61  						math.Float64bits(-123.4), math.Float64bits(-10000000000.0),
    62  					},
    63  					expResults: []uint64{
    64  						1,
    65  						200,
    66  						uint64(math.Float32bits(3.0)),
    67  						math.Float64bits(-123.4),
    68  					},
    69  				},
    70  			},
    71  		},
    72  		{
    73  			name: "swap", m: testcases.SwapParamAndReturn.Module,
    74  			calls: []callCase{
    75  				{params: []uint64{math.MaxUint32, math.MaxInt32}, expResults: []uint64{math.MaxInt32, math.MaxUint32}},
    76  			},
    77  		},
    78  		{
    79  			name: "consts", m: testcases.Constants.Module,
    80  			calls: []callCase{
    81  				{expResults: []uint64{1, 2, uint64(math.Float32bits(32.0)), math.Float64bits(64.0)}},
    82  			},
    83  		},
    84  		{
    85  			name: "unreachable", m: testcases.Unreachable.Module,
    86  			calls: []callCase{{expErr: "unreachable"}},
    87  		},
    88  		{
    89  			name: "add_sub_return", m: testcases.AddSubReturn.Module,
    90  			calls: []callCase{
    91  				{
    92  					params:     []uint64{},
    93  					expResults: []uint64{3, 3, 3, 3},
    94  				},
    95  			},
    96  		},
    97  		{
    98  			name: "arithm_return", m: testcases.ArithmReturn.Module,
    99  			calls: []callCase{
   100  				{
   101  					params: []uint64{
   102  						21, 10, 0xf0,
   103  						21, 10, 0xf0,
   104  					},
   105  					expResults: []uint64{
   106  						21 * 10, 21 & 10, 21 | 10, 21 ^ 10,
   107  						21 << 10, 21 >> 10, 0xf0 >> 10,
   108  						88080384, 20971520,
   109  
   110  						21 * 10, 21 & 10, 21 | 10, 21 ^ 10,
   111  						21 << 10, 21 >> 10, 0xf0 >> 10,
   112  						378302368699121664, 20971520,
   113  					},
   114  				},
   115  			},
   116  		},
   117  		{
   118  			name: "divrem_unsigned_return", m: testcases.DivUReturn32.Module,
   119  			calls: []callCase{
   120  				{
   121  					params:     []uint64{21, 10, 21, 10},
   122  					expResults: []uint64{21 / 10, 21 % 10},
   123  				},
   124  				{
   125  					params: []uint64{21, 0, 1, 1},
   126  					expErr: "wasm error: integer divide by zero",
   127  				},
   128  				{
   129  					params: []uint64{1, 1, 21, 0},
   130  					expErr: "wasm error: integer divide by zero",
   131  				},
   132  				{
   133  					params: []uint64{
   134  						0x80000000, 0xffffffff, 1, 1,
   135  					},
   136  					expResults: []uint64{0, 0},
   137  				},
   138  				{
   139  					params: []uint64{
   140  						1, 1, 0x80000000, 0xffffffff,
   141  					},
   142  					expResults: []uint64{1, 0x80000000},
   143  				},
   144  			},
   145  		},
   146  		{
   147  			name: "divrem_unsigned_return", m: testcases.DivUReturn64.Module,
   148  			calls: []callCase{
   149  				{
   150  					params:     []uint64{21, 10, 21, 10},
   151  					expResults: []uint64{21 / 10, 21 % 10},
   152  				},
   153  				{
   154  					params: []uint64{21, 0, 1, 1},
   155  					expErr: "wasm error: integer divide by zero",
   156  				},
   157  				{
   158  					params: []uint64{1, 1, 21, 0},
   159  					expErr: "wasm error: integer divide by zero",
   160  				},
   161  				{
   162  					params:     []uint64{0x80000000, 0xffffffff, 1, 1},
   163  					expResults: []uint64{0, 0},
   164  				},
   165  				{
   166  					params:     []uint64{1, 1, 0x80000000, 0xffffffff},
   167  					expResults: []uint64{1, 0x80000000},
   168  				},
   169  			},
   170  		},
   171  		{
   172  			name: "divrem_signed_return32", m: testcases.DivSReturn32.Module,
   173  			calls: []callCase{
   174  				{
   175  					params:     []uint64{21, 10, 21, 10},
   176  					expResults: []uint64{21 / 10, 21 % 10},
   177  				},
   178  				{
   179  					params: []uint64{21, 0, 1, 1},
   180  					expErr: "wasm error: integer divide by zero",
   181  				},
   182  				{
   183  					params: []uint64{1, 1, 21, 0},
   184  					expErr: "wasm error: integer divide by zero",
   185  				},
   186  				{
   187  					params: []uint64{0x80000000, 0xffffffff, 1, 1},
   188  					expErr: "wasm error: integer overflow",
   189  				},
   190  				{
   191  					params:     []uint64{1, 1, 0x80000000, 0xffffffff},
   192  					expResults: []uint64{1, 0},
   193  				},
   194  			},
   195  		},
   196  
   197  		{
   198  			name: "divrem_signed_return32 inverted rem div order", m: testcases.DivSReturn32_weird.Module,
   199  			calls: []callCase{
   200  				{
   201  					params: []uint64{21, 0, 1, 1},
   202  					expErr: "wasm error: integer divide by zero",
   203  				},
   204  				{
   205  					params: []uint64{1, 1, 21, 0},
   206  					expErr: "wasm error: integer divide by zero",
   207  				},
   208  				{
   209  					params: []uint64{0x80000000, 0xffffffff, 1, 1},
   210  					expErr: "wasm error: integer overflow",
   211  				},
   212  				{
   213  					params:     []uint64{1, 1, 0x80000000, 0xffffffff},
   214  					expResults: []uint64{0, 1},
   215  				},
   216  			},
   217  		},
   218  		{
   219  			name: "divrem_signed_return64", m: testcases.DivSReturn64.Module,
   220  			calls: []callCase{
   221  				{
   222  					params:     []uint64{21, 10, 21, 10},
   223  					expResults: []uint64{21 / 10, 21 % 10},
   224  				},
   225  				{
   226  					params: []uint64{21, 0, 1, 1},
   227  					expErr: "wasm error: integer divide by zero",
   228  				},
   229  				{
   230  					params: []uint64{1, 1, 21, 0},
   231  					expErr: "wasm error: integer divide by zero",
   232  				},
   233  				{
   234  					params: []uint64{0x8000000000000000, 0xffffffffffffffff, 1, 1},
   235  					expErr: "wasm error: integer overflow",
   236  				},
   237  			},
   238  		},
   239  		{
   240  			name: "integer bit counts", m: testcases.IntegerBitCounts.Module,
   241  			calls: []callCase{{
   242  				params: []uint64{10, 100},
   243  				expResults: []uint64{
   244  					28, 1, 2, 57, 2, 3,
   245  				},
   246  			}},
   247  		},
   248  		{
   249  			name: "many_params_many_results",
   250  			m:    testcases.ManyParamsManyResults.Module,
   251  			calls: []callCase{
   252  				{
   253  					params: []uint64{
   254  						1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
   255  						1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
   256  						1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
   257  						1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
   258  					},
   259  					expResults: []uint64{
   260  						10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
   261  						10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
   262  						10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
   263  						10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
   264  					},
   265  				},
   266  			},
   267  		},
   268  		{
   269  			name: "float_arithm", m: testcases.FloatArithm.Module,
   270  			calls: []callCase{
   271  				{
   272  					params: []uint64{
   273  						math.Float64bits(25), math.Float64bits(5), math.Float64bits(1.4),
   274  						uint64(math.Float32bits(25)), uint64(math.Float32bits(5)), uint64(math.Float32bits(1.4)),
   275  					},
   276  					expResults: []uint64{
   277  						math.Float64bits(-25),
   278  						math.Float64bits(25),
   279  
   280  						math.Float64bits(5),
   281  						math.Float64bits(30),
   282  						math.Float64bits(20),
   283  						math.Float64bits(125),
   284  						math.Float64bits(5),
   285  
   286  						math.Float64bits(1),
   287  						math.Float64bits(1),
   288  						math.Float64bits(2),
   289  						math.Float64bits(1),
   290  
   291  						math.Float64bits(-25),
   292  
   293  						uint64(math.Float32bits(-25)),
   294  						uint64(math.Float32bits(25)),
   295  
   296  						uint64(math.Float32bits(5)),
   297  						uint64(math.Float32bits(30)),
   298  						uint64(math.Float32bits(20)),
   299  						uint64(math.Float32bits(125)),
   300  						uint64(math.Float32bits(5)),
   301  
   302  						uint64(math.Float32bits(1)),
   303  						uint64(math.Float32bits(1)),
   304  						uint64(math.Float32bits(2)),
   305  						uint64(math.Float32bits(1)),
   306  
   307  						uint64(math.Float32bits(-25)),
   308  					},
   309  				},
   310  			},
   311  		},
   312  		{
   313  			name: "min_max_float", m: testcases.MinMaxFloat.Module,
   314  			calls: []callCase{
   315  				{
   316  					params: []uint64{
   317  						math.Float64bits(25), math.Float64bits(5),
   318  						uint64(math.Float32bits(25)), uint64(math.Float32bits(5)),
   319  					},
   320  					expResults: []uint64{
   321  						math.Float64bits(5),
   322  						math.Float64bits(25),
   323  						uint64(math.Float32bits(5)),
   324  						uint64(math.Float32bits(25)),
   325  					},
   326  				},
   327  				{
   328  					// Left-hand side is NaN.
   329  					params: []uint64{
   330  						0x7ff8000000000001, math.Float64bits(5),
   331  						uint64(0x7ff80001), uint64(math.Float32bits(5)),
   332  					},
   333  					expResults: []uint64{
   334  						0x7ff8000000000001,
   335  						0x7ff8000000000001,
   336  
   337  						uint64(0x7ff80001),
   338  						uint64(0x7ff80001),
   339  					},
   340  				},
   341  				{
   342  					// Both NaN.
   343  					params: []uint64{
   344  						0x7ff8000000000001, 0x7ff8000000000001,
   345  						uint64(0x7ff80001), uint64(0x7ff80001),
   346  					},
   347  					expResults: []uint64{
   348  						0x7ff8000000000001,
   349  						0x7ff8000000000001,
   350  
   351  						uint64(0x7ff80001),
   352  						uint64(0x7ff80001),
   353  					},
   354  				},
   355  				{
   356  					// Negative zero and zero.
   357  					params: []uint64{
   358  						0x8000000000000000, 0,
   359  						uint64(0), uint64(0x80000000),
   360  					},
   361  					expResults: []uint64{
   362  						0x8000000000000000,
   363  						0,
   364  
   365  						uint64(0x80000000),
   366  						uint64(0),
   367  					},
   368  				},
   369  			},
   370  		},
   371  		{
   372  			name: "fibonacci_recursive", m: testcases.FibonacciRecursive.Module,
   373  			calls: []callCase{
   374  				{params: []uint64{0}, expResults: []uint64{0}},
   375  				{params: []uint64{1}, expResults: []uint64{1}},
   376  				{params: []uint64{2}, expResults: []uint64{1}},
   377  				{params: []uint64{10}, expResults: []uint64{55}},
   378  				{params: []uint64{20}, expResults: []uint64{6765}},
   379  				{params: []uint64{30}, expResults: []uint64{0xcb228}},
   380  			},
   381  		},
   382  		{name: "call_simple", m: testcases.CallSimple.Module, calls: []callCase{{expResults: []uint64{40}}}},
   383  		{name: "call", m: testcases.Call.Module, calls: []callCase{{expResults: []uint64{45, 45}}}},
   384  		{
   385  			name: "stack overflow",
   386  			m: &wasm.Module{
   387  				TypeSection:     []wasm.FunctionType{{}},
   388  				FunctionSection: []wasm.Index{0},
   389  				CodeSection:     []wasm.Code{{Body: []byte{wasm.OpcodeCall, 0, wasm.OpcodeEnd}}},
   390  				ExportSection:   []wasm.Export{{Name: testcases.ExportedFunctionName, Index: 0, Type: wasm.ExternTypeFunc}},
   391  			},
   392  			calls: []callCase{
   393  				{expErr: "stack overflow"}, {expErr: "stack overflow"}, {expErr: "stack overflow"}, {expErr: "stack overflow"},
   394  			},
   395  		},
   396  		{
   397  			name:     "imported_function_call",
   398  			imported: testcases.ImportedFunctionCall.Imported,
   399  			m:        testcases.ImportedFunctionCall.Module,
   400  			calls: []callCase{
   401  				{params: []uint64{0}, expResults: []uint64{0}},
   402  				{params: []uint64{2}, expResults: []uint64{2 * 2}},
   403  				{params: []uint64{45}, expResults: []uint64{45 * 45}},
   404  				{params: []uint64{90}, expResults: []uint64{90 * 90}},
   405  				{params: []uint64{100}, expResults: []uint64{100 * 100}},
   406  				{params: []uint64{100, 200}, expResults: []uint64{100 * 200}, funcName: "imported_exported"},
   407  			},
   408  		},
   409  		{
   410  			name: "memory_store_basic",
   411  			m:    testcases.MemoryStoreBasic.Module,
   412  			calls: []callCase{
   413  				{params: []uint64{0, 0xf}, expResults: []uint64{0xf}},
   414  				{params: []uint64{256, 0xff}, expResults: []uint64{0xff}},
   415  				{params: []uint64{100, 0xffffffff}, expResults: []uint64{0xffffffff}},
   416  				// We load I32, so we can't load from the last 3 bytes.
   417  				{params: []uint64{uint64(wasm.MemoryPageSize) - 3, 0}, expErr: "out of bounds memory access"},
   418  			},
   419  		},
   420  		{
   421  			name: "memory_load_basic",
   422  			m:    testcases.MemoryLoadBasic.Module,
   423  			calls: []callCase{
   424  				{params: []uint64{0}, expResults: []uint64{0x03_02_01_00}},
   425  				{params: []uint64{256}, expResults: []uint64{0x03_02_01_00}},
   426  				{params: []uint64{100}, expResults: []uint64{103<<24 | 102<<16 | 101<<8 | 100}},
   427  				// Last 4 bytes.
   428  				{params: []uint64{uint64(wasm.MemoryPageSize) - 4}, expResults: []uint64{0xfffefdfc}},
   429  			},
   430  		},
   431  		{
   432  			name: "memory out of bounds",
   433  			m:    testcases.MemoryLoadBasic.Module,
   434  			calls: []callCase{
   435  				{params: []uint64{uint64(wasm.MemoryPageSize)}, expErr: "out of bounds memory access"},
   436  				// We load I32, so we can't load from the last 3 bytes.
   437  				{params: []uint64{uint64(wasm.MemoryPageSize) - 3}, expErr: "out of bounds memory access"},
   438  			},
   439  		},
   440  		{
   441  			name: "memory_loads",
   442  			m:    testcases.MemoryLoads.Module,
   443  			calls: []callCase{
   444  				{params: []uint64{0}, expResults: []uint64{0x3020100, 0x706050403020100, 0x3020100, 0x706050403020100, 0x1211100f, 0x161514131211100f, 0x1211100f, 0x161514131211100f, 0x0, 0xf, 0x0, 0xf, 0x100, 0x100f, 0x100, 0x100f, 0x0, 0xf, 0x0, 0xf, 0x100, 0x100f, 0x100, 0x100f, 0x3020100, 0x1211100f, 0x3020100, 0x1211100f}},
   445  				{params: []uint64{1}, expResults: []uint64{0x4030201, 0x807060504030201, 0x4030201, 0x807060504030201, 0x13121110, 0x1716151413121110, 0x13121110, 0x1716151413121110, 0x1, 0x10, 0x1, 0x10, 0x201, 0x1110, 0x201, 0x1110, 0x1, 0x10, 0x1, 0x10, 0x201, 0x1110, 0x201, 0x1110, 0x4030201, 0x13121110, 0x4030201, 0x13121110}},
   446  				{params: []uint64{8}, expResults: []uint64{0xb0a0908, 0xf0e0d0c0b0a0908, 0xb0a0908, 0xf0e0d0c0b0a0908, 0x1a191817, 0x1e1d1c1b1a191817, 0x1a191817, 0x1e1d1c1b1a191817, 0x8, 0x17, 0x8, 0x17, 0x908, 0x1817, 0x908, 0x1817, 0x8, 0x17, 0x8, 0x17, 0x908, 0x1817, 0x908, 0x1817, 0xb0a0908, 0x1a191817, 0xb0a0908, 0x1a191817}},
   447  				{params: []uint64{0xb}, expResults: []uint64{0xe0d0c0b, 0x1211100f0e0d0c0b, 0xe0d0c0b, 0x1211100f0e0d0c0b, 0x1d1c1b1a, 0x21201f1e1d1c1b1a, 0x1d1c1b1a, 0x21201f1e1d1c1b1a, 0xb, 0x1a, 0xb, 0x1a, 0xc0b, 0x1b1a, 0xc0b, 0x1b1a, 0xb, 0x1a, 0xb, 0x1a, 0xc0b, 0x1b1a, 0xc0b, 0x1b1a, 0xe0d0c0b, 0x1d1c1b1a, 0xe0d0c0b, 0x1d1c1b1a}},
   448  				{params: []uint64{0xc}, expResults: []uint64{0xf0e0d0c, 0x131211100f0e0d0c, 0xf0e0d0c, 0x131211100f0e0d0c, 0x1e1d1c1b, 0x2221201f1e1d1c1b, 0x1e1d1c1b, 0x2221201f1e1d1c1b, 0xc, 0x1b, 0xc, 0x1b, 0xd0c, 0x1c1b, 0xd0c, 0x1c1b, 0xc, 0x1b, 0xc, 0x1b, 0xd0c, 0x1c1b, 0xd0c, 0x1c1b, 0xf0e0d0c, 0x1e1d1c1b, 0xf0e0d0c, 0x1e1d1c1b}},
   449  				{params: []uint64{0xd}, expResults: []uint64{0x100f0e0d, 0x14131211100f0e0d, 0x100f0e0d, 0x14131211100f0e0d, 0x1f1e1d1c, 0x232221201f1e1d1c, 0x1f1e1d1c, 0x232221201f1e1d1c, 0xd, 0x1c, 0xd, 0x1c, 0xe0d, 0x1d1c, 0xe0d, 0x1d1c, 0xd, 0x1c, 0xd, 0x1c, 0xe0d, 0x1d1c, 0xe0d, 0x1d1c, 0x100f0e0d, 0x1f1e1d1c, 0x100f0e0d, 0x1f1e1d1c}},
   450  				{params: []uint64{0xe}, expResults: []uint64{0x11100f0e, 0x1514131211100f0e, 0x11100f0e, 0x1514131211100f0e, 0x201f1e1d, 0x24232221201f1e1d, 0x201f1e1d, 0x24232221201f1e1d, 0xe, 0x1d, 0xe, 0x1d, 0xf0e, 0x1e1d, 0xf0e, 0x1e1d, 0xe, 0x1d, 0xe, 0x1d, 0xf0e, 0x1e1d, 0xf0e, 0x1e1d, 0x11100f0e, 0x201f1e1d, 0x11100f0e, 0x201f1e1d}},
   451  				{params: []uint64{0xf}, expResults: []uint64{0x1211100f, 0x161514131211100f, 0x1211100f, 0x161514131211100f, 0x21201f1e, 0x2524232221201f1e, 0x21201f1e, 0x2524232221201f1e, 0xf, 0x1e, 0xf, 0x1e, 0x100f, 0x1f1e, 0x100f, 0x1f1e, 0xf, 0x1e, 0xf, 0x1e, 0x100f, 0x1f1e, 0x100f, 0x1f1e, 0x1211100f, 0x21201f1e, 0x1211100f, 0x21201f1e}},
   452  			},
   453  		},
   454  		{
   455  			name: "globals_get",
   456  			m:    testcases.GlobalsGet.Module,
   457  			calls: []callCase{
   458  				{expResults: []uint64{0x80000000, 0x8000000000000000, 0x7f7fffff, 0x7fefffffffffffff, 1234, 5678}},
   459  			},
   460  		},
   461  		{
   462  			name: "globals_set",
   463  			m:    testcases.GlobalsSet.Module,
   464  			calls: []callCase{{expResults: []uint64{
   465  				1, 2, uint64(math.Float32bits(3.0)), math.Float64bits(4.0), 10, 20,
   466  			}}},
   467  		},
   468  		{
   469  			name: "globals_mutable",
   470  			m:    testcases.GlobalsMutable.Module,
   471  			calls: []callCase{{expResults: []uint64{
   472  				100, 200, uint64(math.Float32bits(300.0)), math.Float64bits(400.0),
   473  				1, 2, uint64(math.Float32bits(3.0)), math.Float64bits(4.0),
   474  			}}},
   475  		},
   476  		{
   477  			name:  "memory_size_grow",
   478  			m:     testcases.MemorySizeGrow.Module,
   479  			calls: []callCase{{expResults: []uint64{1, 2, 0xffffffff}}},
   480  		},
   481  		{
   482  			name:     "imported_memory_grow",
   483  			imported: testcases.ImportedMemoryGrow.Imported,
   484  			m:        testcases.ImportedMemoryGrow.Module,
   485  			calls:    []callCase{{expResults: []uint64{1, 1, 11, 11}}},
   486  		},
   487  		{
   488  			name: "call_indirect",
   489  			m:    testcases.CallIndirect.Module,
   490  			// parameter == table offset.
   491  			calls: []callCase{
   492  				{params: []uint64{0}, expErr: "indirect call type mismatch"},
   493  				{params: []uint64{1}, expResults: []uint64{10}},
   494  				{params: []uint64{2}, expErr: "indirect call type mismatch"},
   495  				{params: []uint64{10}, expErr: "invalid table access"},             // Null pointer.
   496  				{params: []uint64{math.MaxUint32}, expErr: "invalid table access"}, // Out of bounds.
   497  			},
   498  		},
   499  		{
   500  			name: "br_table",
   501  			m:    testcases.BrTable.Module,
   502  			calls: []callCase{
   503  				{params: []uint64{0}, expResults: []uint64{11}},
   504  				{params: []uint64{1}, expResults: []uint64{12}},
   505  				{params: []uint64{2}, expResults: []uint64{13}},
   506  				{params: []uint64{3}, expResults: []uint64{14}},
   507  				{params: []uint64{4}, expResults: []uint64{15}},
   508  				{params: []uint64{5}, expResults: []uint64{16}},
   509  				// Out of range --> default.
   510  				{params: []uint64{6}, expResults: []uint64{11}},
   511  				{params: []uint64{1000}, expResults: []uint64{11}},
   512  			},
   513  		},
   514  		{
   515  			name: "br_table_with_args",
   516  			m:    testcases.BrTableWithArg.Module,
   517  			calls: []callCase{
   518  				{params: []uint64{0, 100}, expResults: []uint64{11 + 100}},
   519  				{params: []uint64{1, 100}, expResults: []uint64{12 + 100}},
   520  				{params: []uint64{2, 100}, expResults: []uint64{13 + 100}},
   521  				{params: []uint64{3, 100}, expResults: []uint64{14 + 100}},
   522  				{params: []uint64{4, 100}, expResults: []uint64{15 + 100}},
   523  				{params: []uint64{5, 100}, expResults: []uint64{16 + 100}},
   524  				// Out of range --> default.
   525  				{params: []uint64{6, 200}, expResults: []uint64{11 + 200}},
   526  				{params: []uint64{1000, 300}, expResults: []uint64{11 + 300}},
   527  			},
   528  		},
   529  		{
   530  			name: "multi_predecessor_local_ref",
   531  			m:    testcases.MultiPredecessorLocalRef.Module,
   532  			calls: []callCase{
   533  				{params: []uint64{0, 100}, expResults: []uint64{100}},
   534  				{params: []uint64{1, 100}, expResults: []uint64{1}},
   535  				{params: []uint64{1, 200}, expResults: []uint64{1}},
   536  			},
   537  		},
   538  		{
   539  			name: "vector_bit_select",
   540  			m:    testcases.VecBitSelect.Module,
   541  			calls: []callCase{
   542  				{params: []uint64{1, 2, 3, 4, 5, 6}, expResults: []uint64{0x3, 0x2, 0x5, 0x6}},
   543  			},
   544  		},
   545  		{
   546  			name: "vector_shuffle",
   547  			m:    testcases.VecShuffle.Module,
   548  			calls: []callCase{
   549  				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x01010101, 0x04040404}},
   550  				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x03030303, 0x02020202}},
   551  				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0x00000000, 0x00000000}},
   552  				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xffffffff, 0xffffffff}},
   553  				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x00000000, 0xffffffff}},
   554  			},
   555  		},
   556  		{
   557  			name: "vector_shuffle (1st only)",
   558  			m:    testcases.VecShuffleWithLane(1, 1, 1, 1, 0, 0, 0, 0, 10, 10, 10, 10, 0, 0, 0, 0),
   559  			calls: []callCase{
   560  				{params: []uint64{0x0000000000000b0a, 0x0c0000, 0xffffffffffffffff, 0xffffffffffffffff}, expResults: []uint64{0x0a0a0a0a0b0b0b0b, 0x0a0a0a0a0c0c0c0c}},
   561  				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x0101010101010101, 0x101010102020202}},
   562  				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x0303030303030303, 0x303030304040404}},
   563  				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0x0000000000000000, 0x0000000ffffffff}},
   564  				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xffffffffffffffff, 0xffffffff00000000}},
   565  				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x0000000000000000, 0x0000000011111111}},
   566  			},
   567  		},
   568  		{
   569  			name: "vector_shuffle (2nd only)",
   570  			m:    testcases.VecShuffleWithLane(17, 17, 17, 17, 16, 16, 16, 16, 26, 26, 26, 26, 16, 16, 16, 16),
   571  			calls: []callCase{
   572  				{params: []uint64{0xffffffffffffffff, 0xffffffffffffffff, 0x0000000000000b0a, 0x0c0000}, expResults: []uint64{0x0a0a0a0a0b0b0b0b, 0x0a0a0a0a0c0c0c0c}},
   573  				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x303030303030303, 0x303030304040404}},
   574  				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x101010101010101, 0x101010102020202}},
   575  				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0xffffffffffffffff, 0xffffffff00000000}},
   576  				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0x0000000000000000, 0xffffffff}},
   577  				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x1111111111111111, 0x11111111ffffffff}},
   578  			},
   579  		},
   580  		{
   581  			name: "vector_shuffle (mixed)",
   582  			m:    testcases.VecShuffleWithLane(0, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31),
   583  			calls: []callCase{
   584  				{params: []uint64{0xff08ff07ff06ff05, 0xff04ff03ff02ff01, 0x18ff17ff16ff15ff, 0x14ff13ff12ff11ff}, expResults: []uint64{0x1808170716061505, 0x1404130312021101}},
   585  				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x3010301, 0x4020402}},
   586  				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x1030103, 0x2040204}},
   587  				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0xff00ff00, 0xff00ff}},
   588  				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xff00ff, 0xff00ff00}},
   589  				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x11001100, 0xff11ff11}},
   590  			},
   591  		},
   592  		{
   593  			name:     "memory_wait32",
   594  			m:        testcases.MemoryWait32.Module,
   595  			features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads,
   596  			calls: []callCase{
   597  				{params: []uint64{0x0, 0xbeef, 0xffffffff}, expResults: []uint64{1}}, // exp not equal, returns 1
   598  				{params: []uint64{0x1, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"},
   599  				{params: []uint64{0x2, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"},
   600  				{params: []uint64{0x3, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"},
   601  				{params: []uint64{0x4, 0xbeef, 0xffffffff}, expResults: []uint64{1}}, // exp not equal, returns 1
   602  
   603  				{params: []uint64{0xffffffff, 0xbeef, 0xffffffff}, expErr: "out of bounds memory access"},
   604  			},
   605  		},
   606  		{
   607  			name:     "memory_wait64",
   608  			m:        testcases.MemoryWait64.Module,
   609  			features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads,
   610  			calls: []callCase{
   611  				{params: []uint64{0x0, 0xbeef, 0xffffffff}, expResults: []uint64{1}}, // exp not equal, returns 1
   612  				{params: []uint64{0x1, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"},
   613  				{params: []uint64{0x2, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"},
   614  				{params: []uint64{0x3, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"},
   615  				{params: []uint64{0x4, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"},
   616  				{params: []uint64{0x5, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"},
   617  				{params: []uint64{0x6, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"},
   618  				{params: []uint64{0x7, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"},
   619  				{params: []uint64{0x8, 0xbeef, 0xffffffff}, expResults: []uint64{1}}, // exp not equal, returns 1
   620  
   621  				{params: []uint64{0xffffffff, 0xbeef, 0xffffffff}, expErr: "out of bounds memory access"},
   622  			},
   623  		},
   624  		{
   625  			name:     "memory_notify",
   626  			m:        testcases.MemoryNotify.Module,
   627  			features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads,
   628  			calls: []callCase{
   629  				{params: []uint64{0x0, 0x1}, expResults: []uint64{0}}, // no waiters, returns 0
   630  				{params: []uint64{0x1, 0x1}, expErr: "unaligned atomic"},
   631  				{params: []uint64{0x2, 0x1}, expErr: "unaligned atomic"},
   632  				{params: []uint64{0x3, 0x1}, expErr: "unaligned atomic"},
   633  				{params: []uint64{0x4, 0x1}, expResults: []uint64{0}}, // no waiters, returns 0
   634  			},
   635  		},
   636  		{
   637  			name:     "atomic_rmw_add",
   638  			m:        testcases.AtomicRmwAdd.Module,
   639  			features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads,
   640  			calls: []callCase{
   641  				{params: []uint64{1, 2, 3, 4, 5, 6, 7}, expResults: []uint64{0, 0, 0, 0, 0, 0, 0}},
   642  				{params: []uint64{1, 2, 3, 4, 5, 6, 7}, expResults: []uint64{1, 2, 3, 4, 5, 6, 7}},
   643  				{params: []uint64{1, 2, 3, 4, 5, 6, 7}, expResults: []uint64{2, 4, 6, 8, 10, 12, 14}},
   644  			},
   645  		},
   646  		{
   647  			name:     "atomic_rmw_sub",
   648  			m:        testcases.AtomicRmwSub.Module,
   649  			features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads,
   650  			calls: []callCase{
   651  				{params: []uint64{1, 2, 3, 4, 5, 6, 7}, expResults: []uint64{0, 0, 0, 0, 0, 0, 0}},
   652  				{
   653  					params: []uint64{1, 2, 3, 4, 5, 6, 7},
   654  					expResults: []uint64{
   655  						api.EncodeI32(-1) & 0xff,
   656  						api.EncodeI32(-2) & 0xffff,
   657  						api.EncodeI32(-3),
   658  						api.EncodeI64(-4) & 0xff,
   659  						api.EncodeI64(-5) & 0xffff,
   660  						api.EncodeI64(-6) & 0xffffffff,
   661  						api.EncodeI64(-7),
   662  					},
   663  				},
   664  				{
   665  					params: []uint64{1, 2, 3, 4, 5, 6, 7},
   666  					expResults: []uint64{
   667  						api.EncodeI32(-2) & 0xff,
   668  						api.EncodeI32(-4) & 0xffff,
   669  						api.EncodeI32(-6),
   670  						api.EncodeI64(-8) & 0xff,
   671  						api.EncodeI64(-10) & 0xffff,
   672  						api.EncodeI64(-12) & 0xffffffff,
   673  						api.EncodeI64(-14),
   674  					},
   675  				},
   676  			},
   677  		},
   678  		{
   679  			name:     "atomic_rmw_and",
   680  			m:        testcases.AtomicRmwAnd.Module,
   681  			features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads,
   682  			setupMemory: func(mem api.Memory) {
   683  				mem.WriteUint32Le(0, 0xffffffff)
   684  				mem.WriteUint32Le(8, 0xffffffff)
   685  				mem.WriteUint32Le(16, 0xffffffff)
   686  				mem.WriteUint64Le(24, 0xffffffffffffffff)
   687  				mem.WriteUint64Le(32, 0xffffffffffffffff)
   688  				mem.WriteUint64Le(40, 0xffffffffffffffff)
   689  				mem.WriteUint64Le(48, 0xffffffffffffffff)
   690  			},
   691  			calls: []callCase{
   692  				{
   693  					params:     []uint64{0xfffffffe, 0xfffffffe, 0xfffffffe, 0xfffffffffffffffe, 0xfffffffffffffffe, 0xfffffffffffffffe, 0xfffffffffffffffe},
   694  					expResults: []uint64{0xff, 0xffff, 0xffffffff, 0xff, 0xffff, 0xffffffff, 0xffffffffffffffff},
   695  				},
   696  				{
   697  					params:     []uint64{0xffffffee, 0xffffffee, 0xffffffee, 0xffffffffffffffee, 0xffffffffffffffee, 0xffffffffffffffee, 0xffffffffffffffee},
   698  					expResults: []uint64{0xfe, 0xfffe, 0xfffffffe, 0xfe, 0xfffe, 0xfffffffe, 0xfffffffffffffffe},
   699  				},
   700  				{
   701  					params:     []uint64{0xffffffee, 0xffffffee, 0xffffffee, 0xffffffffffffffee, 0xffffffffffffffee, 0xffffffffffffffee, 0xffffffffffffffee},
   702  					expResults: []uint64{0xee, 0xffee, 0xffffffee, 0xee, 0xffee, 0xffffffee, 0xffffffffffffffee},
   703  				},
   704  			},
   705  		},
   706  		{
   707  			name:     "atomic_rmw_or",
   708  			m:        testcases.AtomicRmwOr.Module,
   709  			features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads,
   710  			calls: []callCase{
   711  				{
   712  					params:     []uint64{0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f},
   713  					expResults: []uint64{0, 0, 0, 0, 0, 0, 0},
   714  				},
   715  				{
   716  					params:     []uint64{0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf},
   717  					expResults: []uint64{0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f},
   718  				},
   719  				{
   720  					params:     []uint64{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
   721  					expResults: []uint64{0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf},
   722  				},
   723  			},
   724  		},
   725  		{
   726  			name:     "atomic_rmw_xor",
   727  			m:        testcases.AtomicRmwXor.Module,
   728  			features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads,
   729  			calls: []callCase{
   730  				{
   731  					params:     []uint64{0, 0, 0, 0, 0, 0, 0},
   732  					expResults: []uint64{0, 0, 0, 0, 0, 0, 0},
   733  				},
   734  				{
   735  					params:     []uint64{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff},
   736  					expResults: []uint64{0, 0, 0, 0, 0, 0, 0},
   737  				},
   738  				{
   739  					params:     []uint64{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff},
   740  					expResults: []uint64{0xff, 0xffff, 0xffffffff, 0xff, 0xffff, 0xffffffff, 0xffffffffffffffff},
   741  				},
   742  				{
   743  					params:     []uint64{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff},
   744  					expResults: []uint64{0, 0, 0, 0, 0, 0, 0},
   745  				},
   746  			},
   747  		},
   748  		{
   749  			name:     "atomic_rmw_xchg",
   750  			m:        testcases.AtomicRmwXchg.Module,
   751  			features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads,
   752  			calls: []callCase{
   753  				{
   754  					params:     []uint64{1, 2, 3, 4, 5, 6, 7},
   755  					expResults: []uint64{0, 0, 0, 0, 0, 0, 0},
   756  				},
   757  				{
   758  					params:     []uint64{2, 3, 4, 5, 6, 7, 8},
   759  					expResults: []uint64{1, 2, 3, 4, 5, 6, 7},
   760  				},
   761  				{
   762  					params:     []uint64{2, 3, 4, 5, 6, 7, 8},
   763  					expResults: []uint64{2, 3, 4, 5, 6, 7, 8},
   764  				},
   765  			},
   766  		},
   767  		{
   768  			name:     "atomic_cas",
   769  			m:        testcases.AtomicCas.Module,
   770  			features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads,
   771  			calls: []callCase{
   772  				// no store
   773  				{
   774  					params:     []uint64{1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2},
   775  					expResults: []uint64{0, 0, 0, 0, 0, 0, 0},
   776  				},
   777  				// store
   778  				{
   779  					params:     []uint64{0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2},
   780  					expResults: []uint64{0, 0, 0, 0, 0, 0, 0},
   781  				},
   782  				// store
   783  				{
   784  					params:     []uint64{2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3},
   785  					expResults: []uint64{2, 2, 2, 2, 2, 2, 2},
   786  				},
   787  				// no store
   788  				{
   789  					params:     []uint64{2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4},
   790  					expResults: []uint64{3, 3, 3, 3, 3, 3, 3},
   791  				},
   792  			},
   793  		},
   794  		{
   795  			// Checks if load works when comparison value is zero. It wouldn't if
   796  			// the zero register gets used.
   797  			name:     "atomic_cas_const0",
   798  			m:        testcases.AtomicCasConst0.Module,
   799  			features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads,
   800  			setupMemory: func(mem api.Memory) {
   801  				mem.WriteUint32Le(0, 1)
   802  				mem.WriteUint32Le(8, 2)
   803  				mem.WriteUint32Le(16, 3)
   804  				mem.WriteUint64Le(24, 4)
   805  				mem.WriteUint64Le(32, 5)
   806  				mem.WriteUint64Le(40, 6)
   807  				mem.WriteUint64Le(48, 7)
   808  			},
   809  			calls: []callCase{
   810  				{
   811  					params:     []uint64{8, 9, 10, 11, 12, 13, 14},
   812  					expResults: []uint64{1, 2, 3, 4, 5, 6, 7},
   813  				},
   814  			},
   815  		},
   816  		{
   817  			name:     "atomic_store_load",
   818  			m:        testcases.AtomicStoreLoad.Module,
   819  			features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads,
   820  			calls: []callCase{
   821  				{params: []uint64{1, 2, 3, 4, 5, 6, 7}, expResults: []uint64{1, 2, 3, 4, 5, 6, 7}},
   822  				{params: []uint64{10, 20, 30, 40, 50, 60, 70}, expResults: []uint64{10, 20, 30, 40, 50, 60, 70}},
   823  			},
   824  		},
   825  		{
   826  			name:     "atomic_fence",
   827  			m:        testcases.AtomicFence.Module,
   828  			features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads,
   829  			calls: []callCase{
   830  				{params: []uint64{}, expResults: []uint64{}},
   831  			},
   832  		},
   833  		{
   834  			name: "float_le",
   835  			m:    testcases.FloatLe.Module,
   836  			calls: []callCase{
   837  				{params: []uint64{math.Float64bits(1.0)}, expResults: []uint64{1, 1}},
   838  				{params: []uint64{math.Float64bits(0.0)}, expResults: []uint64{1, 1}},
   839  				{params: []uint64{math.Float64bits(1.1)}, expResults: []uint64{0, 0}},
   840  				{params: []uint64{math.Float64bits(math.NaN())}, expResults: []uint64{0, 0}},
   841  			},
   842  		},
   843  	} {
   844  		tc := tc
   845  		t.Run(tc.name, func(t *testing.T) {
   846  			for i := 0; i < 2; i++ {
   847  				var name string
   848  				if i == 0 {
   849  					name = "no cache"
   850  				} else {
   851  					name = "with cache"
   852  				}
   853  				t.Run(name, func(t *testing.T) {
   854  					cache, err := wazero.NewCompilationCacheWithDir(tmp)
   855  					require.NoError(t, err)
   856  					config := wazero.NewRuntimeConfigCompiler().WithCompilationCache(cache)
   857  					if tc.features != 0 {
   858  						config = config.WithCoreFeatures(tc.features)
   859  					}
   860  
   861  					ctx := context.Background()
   862  					r := wazero.NewRuntimeWithConfig(ctx, config)
   863  					defer func() {
   864  						require.NoError(t, r.Close(ctx))
   865  					}()
   866  
   867  					if tc.imported != nil {
   868  						imported, err := r.CompileModule(ctx, binaryencoding.EncodeModule(tc.imported))
   869  						require.NoError(t, err)
   870  
   871  						_, err = r.InstantiateModule(ctx, imported, wazero.NewModuleConfig())
   872  						require.NoError(t, err)
   873  					}
   874  
   875  					compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(tc.m))
   876  					require.NoError(t, err)
   877  
   878  					inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
   879  					require.NoError(t, err)
   880  
   881  					if tc.setupMemory != nil {
   882  						tc.setupMemory(inst.Memory())
   883  					}
   884  
   885  					for _, cc := range tc.calls {
   886  						name := cc.funcName
   887  						if name == "" {
   888  							name = testcases.ExportedFunctionName
   889  						}
   890  						t.Run(fmt.Sprintf("call_%s%v", name, cc.params), func(t *testing.T) {
   891  							f := inst.ExportedFunction(name)
   892  							require.NotNil(t, f)
   893  							result, err := f.Call(ctx, cc.params...)
   894  							if cc.expErr != "" {
   895  								require.Contains(t, err.Error(), cc.expErr)
   896  							} else {
   897  								require.NoError(t, err)
   898  								require.Equal(t, len(cc.expResults), len(result))
   899  								for i := range cc.expResults {
   900  									if cc.expResults[i] != result[i] {
   901  										t.Errorf("result[%d]: exp %x, got %x", i, cc.expResults[i], result[i])
   902  									}
   903  								}
   904  							}
   905  						})
   906  					}
   907  				})
   908  			}
   909  		})
   910  	}
   911  }
   912  
   913  func TestE2E_host_functions(t *testing.T) {
   914  	var buf bytes.Buffer
   915  	ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf))
   916  
   917  	for _, tc := range []struct {
   918  		name string
   919  		ctx  context.Context
   920  	}{
   921  		{name: "listener", ctx: ctx},
   922  		{name: "no listener", ctx: context.Background()},
   923  	} {
   924  		tc := tc
   925  		t.Run(tc.name, func(t *testing.T) {
   926  			ctx := tc.ctx
   927  
   928  			config := wazero.NewRuntimeConfigCompiler()
   929  
   930  			r := wazero.NewRuntimeWithConfig(ctx, config)
   931  			defer func() {
   932  				require.NoError(t, r.Close(ctx))
   933  			}()
   934  
   935  			var expectedMod api.Module
   936  
   937  			b := r.NewHostModuleBuilder("env")
   938  			b.NewFunctionBuilder().WithFunc(func(ctx2 context.Context, d float64) float64 {
   939  				require.Equal(t, ctx, ctx2)
   940  				require.Equal(t, 35.0, d)
   941  				return math.Sqrt(d)
   942  			}).Export("root")
   943  			b.NewFunctionBuilder().WithFunc(func(ctx2 context.Context, mod api.Module, a uint32, b uint64, c float32, d float64) (uint32, uint64, float32, float64) {
   944  				require.Equal(t, expectedMod, mod)
   945  				require.Equal(t, ctx, ctx2)
   946  				require.Equal(t, uint32(2), a)
   947  				require.Equal(t, uint64(100), b)
   948  				require.Equal(t, float32(15.0), c)
   949  				require.Equal(t, 35.0, d)
   950  				return a * a, b * b, c * c, d * d
   951  			}).Export("square")
   952  
   953  			_, err := b.Instantiate(ctx)
   954  			require.NoError(t, err)
   955  
   956  			m := &wasm.Module{
   957  				ImportFunctionCount: 2,
   958  				ImportSection: []wasm.Import{
   959  					{Module: "env", Name: "root", Type: wasm.ExternTypeFunc, DescFunc: 0},
   960  					{Module: "env", Name: "square", Type: wasm.ExternTypeFunc, DescFunc: 1},
   961  				},
   962  				TypeSection: []wasm.FunctionType{
   963  					{Results: []wasm.ValueType{f64}, Params: []wasm.ValueType{f64}},
   964  					{Results: []wasm.ValueType{i32, i64, f32, f64}, Params: []wasm.ValueType{i32, i64, f32, f64}},
   965  					{Results: []wasm.ValueType{i32, i64, f32, f64, f64}, Params: []wasm.ValueType{i32, i64, f32, f64}},
   966  				},
   967  				FunctionSection: []wasm.Index{2},
   968  				CodeSection: []wasm.Code{{
   969  					Body: []byte{
   970  						wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 1, wasm.OpcodeLocalGet, 2, wasm.OpcodeLocalGet, 3,
   971  						wasm.OpcodeCall, 1,
   972  						wasm.OpcodeLocalGet, 3,
   973  						wasm.OpcodeCall, 0,
   974  						wasm.OpcodeEnd,
   975  					},
   976  				}},
   977  				ExportSection: []wasm.Export{{Name: testcases.ExportedFunctionName, Type: wasm.ExternTypeFunc, Index: 2}},
   978  			}
   979  
   980  			compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(m))
   981  			require.NoError(t, err)
   982  
   983  			inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
   984  			require.NoError(t, err)
   985  
   986  			expectedMod = inst
   987  
   988  			f := inst.ExportedFunction(testcases.ExportedFunctionName)
   989  
   990  			res, err := f.Call(ctx, []uint64{2, 100, uint64(math.Float32bits(15.0)), math.Float64bits(35.0)}...)
   991  			require.NoError(t, err)
   992  			require.Equal(t, []uint64{
   993  				2 * 2, 100 * 100, uint64(math.Float32bits(15.0 * 15.0)), math.Float64bits(35.0 * 35.0),
   994  				math.Float64bits(math.Sqrt(35.0)),
   995  			}, res)
   996  		})
   997  	}
   998  
   999  	require.Equal(t, `
  1000  --> .$2(2,100,15,35)
  1001  	==> env.square(2,100,15,35)
  1002  	<== (4,10000,225,1225)
  1003  	==> env.root(35)
  1004  	<== 5.916079783099616
  1005  <-- (4,10000,225,1225,5.916079783099616)
  1006  `, "\n"+buf.String())
  1007  }
  1008  
  1009  func TestE2E_stores(t *testing.T) {
  1010  	config := wazero.NewRuntimeConfigCompiler()
  1011  
  1012  	ctx := context.Background()
  1013  	r := wazero.NewRuntimeWithConfig(ctx, config)
  1014  	defer func() {
  1015  		require.NoError(t, r.Close(ctx))
  1016  	}()
  1017  
  1018  	compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(testcases.MemoryStores.Module))
  1019  	require.NoError(t, err)
  1020  
  1021  	inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
  1022  	require.NoError(t, err)
  1023  
  1024  	f := inst.ExportedFunction(testcases.ExportedFunctionName)
  1025  
  1026  	mem, ok := inst.Memory().Read(0, wasm.MemoryPageSize)
  1027  	require.True(t, ok)
  1028  	for _, tc := range []struct {
  1029  		i32 uint32
  1030  		i64 uint64
  1031  		f32 float32
  1032  		f64 float64
  1033  	}{
  1034  		{0, 0, 0, 0},
  1035  		{1, 2, 3.0, 4.0},
  1036  		{math.MaxUint32, math.MaxUint64, float32(math.NaN()), math.NaN()},
  1037  		{1 << 31, 1 << 63, 3.0, 4.0},
  1038  	} {
  1039  		t.Run(fmt.Sprintf("i32=%#x,i64=%#x,f32=%#x,f64=%#x", tc.i32, tc.i64, tc.f32, tc.f64), func(t *testing.T) {
  1040  			_, err = f.Call(ctx, []uint64{uint64(tc.i32), tc.i64, uint64(math.Float32bits(tc.f32)), math.Float64bits(tc.f64)}...)
  1041  			require.NoError(t, err)
  1042  
  1043  			offset := 0
  1044  			require.Equal(t, binary.LittleEndian.Uint32(mem[offset:]), tc.i32)
  1045  			offset += 8
  1046  			require.Equal(t, binary.LittleEndian.Uint64(mem[offset:]), tc.i64)
  1047  			offset += 8
  1048  			require.Equal(t, math.Float32bits(tc.f32), binary.LittleEndian.Uint32(mem[offset:]))
  1049  			offset += 8
  1050  			require.Equal(t, math.Float64bits(tc.f64), binary.LittleEndian.Uint64(mem[offset:]))
  1051  			offset += 8
  1052  
  1053  			// i32.store_8
  1054  			view := binary.LittleEndian.Uint64(mem[offset:])
  1055  			require.Equal(t, uint64(tc.i32)&0xff, view)
  1056  			offset += 8
  1057  			// i32.store_16
  1058  			view = binary.LittleEndian.Uint64(mem[offset:])
  1059  			require.Equal(t, uint64(tc.i32)&0xffff, view)
  1060  			offset += 8
  1061  			// i64.store_8
  1062  			view = binary.LittleEndian.Uint64(mem[offset:])
  1063  			require.Equal(t, tc.i64&0xff, view)
  1064  			offset += 8
  1065  			// i64.store_16
  1066  			view = binary.LittleEndian.Uint64(mem[offset:])
  1067  			require.Equal(t, tc.i64&0xffff, view)
  1068  			offset += 8
  1069  			// i64.store_32
  1070  			view = binary.LittleEndian.Uint64(mem[offset:])
  1071  			require.Equal(t, tc.i64&0xffffffff, view)
  1072  		})
  1073  	}
  1074  }
  1075  
  1076  func TestE2E_reexported_memory(t *testing.T) {
  1077  	m1 := &wasm.Module{
  1078  		ExportSection: []wasm.Export{{Name: "mem", Type: wasm.ExternTypeMemory, Index: 0}},
  1079  		MemorySection: &wasm.Memory{Min: 1},
  1080  		NameSection:   &wasm.NameSection{ModuleName: "m1"},
  1081  	}
  1082  	m2 := &wasm.Module{
  1083  		ImportMemoryCount: 1,
  1084  		ExportSection:     []wasm.Export{{Name: "mem2", Type: wasm.ExternTypeMemory, Index: 0}},
  1085  		ImportSection:     []wasm.Import{{Module: "m1", Name: "mem", Type: wasm.ExternTypeMemory, DescMem: &wasm.Memory{Min: 1}}},
  1086  		NameSection:       &wasm.NameSection{ModuleName: "m2"},
  1087  	}
  1088  	m3 := &wasm.Module{
  1089  		ImportMemoryCount: 1,
  1090  		ImportSection:     []wasm.Import{{Module: "m2", Name: "mem2", Type: wasm.ExternTypeMemory, DescMem: &wasm.Memory{Min: 1}}},
  1091  		TypeSection:       []wasm.FunctionType{{Results: []wasm.ValueType{i32}}},
  1092  		ExportSection:     []wasm.Export{{Name: testcases.ExportedFunctionName, Type: wasm.ExternTypeFunc, Index: 0}},
  1093  		FunctionSection:   []wasm.Index{0},
  1094  		CodeSection:       []wasm.Code{{Body: []byte{wasm.OpcodeI32Const, 10, wasm.OpcodeMemoryGrow, 0, wasm.OpcodeEnd}}},
  1095  	}
  1096  
  1097  	config := wazero.NewRuntimeConfigCompiler()
  1098  
  1099  	ctx := context.Background()
  1100  	r := wazero.NewRuntimeWithConfig(ctx, config)
  1101  	defer func() {
  1102  		require.NoError(t, r.Close(ctx))
  1103  	}()
  1104  
  1105  	m1Inst, err := r.Instantiate(ctx, binaryencoding.EncodeModule(m1))
  1106  	require.NoError(t, err)
  1107  
  1108  	m2Inst, err := r.Instantiate(ctx, binaryencoding.EncodeModule(m2))
  1109  	require.NoError(t, err)
  1110  
  1111  	m3Inst, err := r.Instantiate(ctx, binaryencoding.EncodeModule(m3))
  1112  	require.NoError(t, err)
  1113  
  1114  	f := m3Inst.ExportedFunction(testcases.ExportedFunctionName)
  1115  	result, err := f.Call(ctx)
  1116  	require.NoError(t, err)
  1117  	require.Equal(t, uint64(1), result[0])
  1118  	mem := m1Inst.Memory()
  1119  	require.Equal(t, mem, m3Inst.Memory())
  1120  	require.Equal(t, mem, m2Inst.Memory())
  1121  	require.Equal(t, uint32(11), mem.Size()/65536)
  1122  }
  1123  
  1124  func TestStackUnwind_panic_in_host(t *testing.T) {
  1125  	unreachable := &wasm.Module{
  1126  		ImportFunctionCount: 1,
  1127  		ImportSection:       []wasm.Import{{Module: "host", Name: "cause_unreachable", Type: wasm.ExternTypeFunc, DescFunc: 0}},
  1128  		TypeSection:         []wasm.FunctionType{{}},
  1129  		ExportSection:       []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 1}},
  1130  		FunctionSection:     []wasm.Index{0, 0, 0},
  1131  		CodeSection: []wasm.Code{
  1132  			{Body: []byte{wasm.OpcodeCall, 2, wasm.OpcodeEnd}},
  1133  			{Body: []byte{wasm.OpcodeCall, 3, wasm.OpcodeEnd}},
  1134  			{Body: []byte{wasm.OpcodeCall, 0, wasm.OpcodeEnd}}, // call host.cause_unreachable.
  1135  		},
  1136  		NameSection: &wasm.NameSection{
  1137  			FunctionNames: wasm.NameMap{
  1138  				wasm.NameAssoc{Index: 0, Name: "host.unreachable"},
  1139  				wasm.NameAssoc{Index: 1, Name: "main"},
  1140  				wasm.NameAssoc{Index: 2, Name: "one"},
  1141  				wasm.NameAssoc{Index: 3, Name: "two"},
  1142  			},
  1143  		},
  1144  	}
  1145  
  1146  	config := wazero.NewRuntimeConfigCompiler()
  1147  
  1148  	ctx := context.Background()
  1149  	r := wazero.NewRuntimeWithConfig(ctx, config)
  1150  	defer func() {
  1151  		require.NoError(t, r.Close(ctx))
  1152  	}()
  1153  
  1154  	callUnreachable := func() {
  1155  		panic("panic in host function")
  1156  	}
  1157  
  1158  	_, err := r.NewHostModuleBuilder("host").
  1159  		NewFunctionBuilder().WithFunc(callUnreachable).Export("cause_unreachable").
  1160  		Instantiate(ctx)
  1161  	require.NoError(t, err)
  1162  
  1163  	module, err := r.Instantiate(ctx, binaryencoding.EncodeModule(unreachable))
  1164  	require.NoError(t, err)
  1165  	defer module.Close(ctx)
  1166  
  1167  	_, err = module.ExportedFunction("main").Call(ctx)
  1168  	exp := `panic in host function (recovered by wazero)
  1169  wasm stack trace:
  1170  	host.cause_unreachable()
  1171  	.two()
  1172  	.one()
  1173  	.main()`
  1174  	require.Equal(t, exp, err.Error())
  1175  }
  1176  
  1177  func TestStackUnwind_unreachable(t *testing.T) {
  1178  	unreachable := &wasm.Module{
  1179  		TypeSection:     []wasm.FunctionType{{}},
  1180  		ExportSection:   []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}},
  1181  		FunctionSection: []wasm.Index{0, 0, 0},
  1182  		CodeSection: []wasm.Code{
  1183  			{Body: []byte{wasm.OpcodeCall, 1, wasm.OpcodeEnd}},
  1184  			{Body: []byte{wasm.OpcodeCall, 2, wasm.OpcodeEnd}},
  1185  			{Body: []byte{wasm.OpcodeUnreachable, wasm.OpcodeEnd}},
  1186  		},
  1187  		NameSection: &wasm.NameSection{
  1188  			FunctionNames: wasm.NameMap{
  1189  				wasm.NameAssoc{Index: 0, Name: "main"},
  1190  				wasm.NameAssoc{Index: 1, Name: "one"},
  1191  				wasm.NameAssoc{Index: 2, Name: "two"},
  1192  			},
  1193  		},
  1194  	}
  1195  
  1196  	config := wazero.NewRuntimeConfigCompiler()
  1197  	ctx := context.Background()
  1198  	r := wazero.NewRuntimeWithConfig(ctx, config)
  1199  	defer func() {
  1200  		require.NoError(t, r.Close(ctx))
  1201  	}()
  1202  
  1203  	module, err := r.Instantiate(ctx, binaryencoding.EncodeModule(unreachable))
  1204  	require.NoError(t, err)
  1205  	defer module.Close(ctx)
  1206  
  1207  	_, err = module.ExportedFunction("main").Call(ctx)
  1208  	exp := `wasm error: unreachable
  1209  wasm stack trace:
  1210  	.two()
  1211  	.one()
  1212  	.main()`
  1213  	require.Equal(t, exp, err.Error())
  1214  }
  1215  
  1216  func TestListener_local(t *testing.T) {
  1217  	var buf bytes.Buffer
  1218  	config := wazero.NewRuntimeConfigCompiler()
  1219  	ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf))
  1220  
  1221  	r := wazero.NewRuntimeWithConfig(ctx, config)
  1222  	defer func() {
  1223  		require.NoError(t, r.Close(ctx))
  1224  	}()
  1225  
  1226  	compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(testcases.CallIndirect.Module))
  1227  	require.NoError(t, err)
  1228  
  1229  	inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
  1230  	require.NoError(t, err)
  1231  
  1232  	res, err := inst.ExportedFunction(testcases.ExportedFunctionName).Call(ctx, 1)
  1233  	require.NoError(t, err)
  1234  	require.Equal(t, []uint64{10}, res)
  1235  
  1236  	require.Equal(t, `
  1237  --> .$0(1)
  1238  	--> .$2()
  1239  	<-- 10
  1240  <-- 10
  1241  `, "\n"+buf.String())
  1242  }
  1243  
  1244  func TestListener_imported(t *testing.T) {
  1245  	var buf bytes.Buffer
  1246  	config := wazero.NewRuntimeConfigCompiler()
  1247  	ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf))
  1248  
  1249  	r := wazero.NewRuntimeWithConfig(ctx, config)
  1250  	defer func() {
  1251  		require.NoError(t, r.Close(ctx))
  1252  	}()
  1253  
  1254  	_, err := r.Instantiate(ctx, binaryencoding.EncodeModule(testcases.ImportedFunctionCall.Imported))
  1255  	require.NoError(t, err)
  1256  
  1257  	compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(testcases.ImportedFunctionCall.Module))
  1258  	require.NoError(t, err)
  1259  
  1260  	inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
  1261  	require.NoError(t, err)
  1262  
  1263  	res, err := inst.ExportedFunction(testcases.ExportedFunctionName).Call(ctx, 100)
  1264  	require.NoError(t, err)
  1265  	require.Equal(t, []uint64{10000}, res)
  1266  
  1267  	require.Equal(t, `
  1268  --> .$1(100)
  1269  	--> env.$0(100,100)
  1270  	<-- 10000
  1271  <-- 10000
  1272  `, "\n"+buf.String())
  1273  }
  1274  
  1275  func TestListener_long(t *testing.T) {
  1276  	pickOneParam := binaryencoding.EncodeModule(&wasm.Module{
  1277  		TypeSection: []wasm.FunctionType{{Results: []wasm.ValueType{i32}, Params: []wasm.ValueType{
  1278  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
  1279  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
  1280  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
  1281  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
  1282  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
  1283  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
  1284  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
  1285  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
  1286  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
  1287  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
  1288  		}}},
  1289  		ExportSection:   []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}},
  1290  		FunctionSection: []wasm.Index{0},
  1291  		CodeSection: []wasm.Code{
  1292  			{Body: []byte{wasm.OpcodeLocalGet, 10, wasm.OpcodeEnd}},
  1293  		},
  1294  	})
  1295  
  1296  	var buf bytes.Buffer
  1297  	config := wazero.NewRuntimeConfigCompiler()
  1298  	ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf))
  1299  
  1300  	r := wazero.NewRuntimeWithConfig(ctx, config)
  1301  	defer func() {
  1302  		require.NoError(t, r.Close(ctx))
  1303  	}()
  1304  
  1305  	inst, err := r.Instantiate(ctx, pickOneParam)
  1306  	require.NoError(t, err)
  1307  
  1308  	f := inst.ExportedFunction("main")
  1309  	require.NotNil(t, f)
  1310  	param := make([]uint64, 100)
  1311  	for i := range param {
  1312  		param[i] = uint64(i)
  1313  	}
  1314  	res, err := f.Call(ctx, param...)
  1315  	require.NoError(t, err)
  1316  	require.Equal(t, []uint64{0xb}, res)
  1317  
  1318  	require.Equal(t, `
  1319  --> .$0(0,1,3e-45,1.5e-323,4,5,6,00000000000000070000000000000008,1.3e-44,10,11,1.7e-44,6.4e-323,14,15,16,00000000000000110000000000000012,2.7e-44,20,21,3.1e-44,1.14e-322,24,25,26,000000000000001b000000000000001c,4e-44,30,31,4.5e-44,1.63e-322,34,35,36,00000000000000250000000000000026,5.5e-44,40,41,5.9e-44,2.1e-322,44,45,46,000000000000002f0000000000000030,6.9e-44,50,51,7.3e-44,2.6e-322,54,55,56,0000000000000039000000000000003a,8.3e-44,60,61,8.7e-44,3.1e-322,64,65,66,00000000000000430000000000000044,9.7e-44,70,71,1.01e-43,3.6e-322,74,75,76,000000000000004d000000000000004e,1.11e-43,80,81,1.15e-43,4.1e-322,84,85,86,00000000000000570000000000000058,1.25e-43,90,91,1.29e-43,4.6e-322,94,95,96,00000000000000610000000000000062,1.39e-43)
  1320  <-- 11
  1321  `, "\n"+buf.String())
  1322  }
  1323  
  1324  func TestListener_long_as_is(t *testing.T) {
  1325  	params := []wasm.ValueType{
  1326  		i32, i64, i32, i64, i32, i64, i32, i64, i32, i64,
  1327  		i32, i64, i32, i64, i32, i64, i32, i64, i32, i64,
  1328  	}
  1329  
  1330  	const paramNum = 20
  1331  
  1332  	var body []byte
  1333  	for i := 0; i < paramNum; i++ {
  1334  		body = append(body, wasm.OpcodeLocalGet)
  1335  		body = append(body, leb128.EncodeUint32(uint32(i))...)
  1336  	}
  1337  	body = append(body, wasm.OpcodeEnd)
  1338  
  1339  	bin := binaryencoding.EncodeModule(&wasm.Module{
  1340  		TypeSection:     []wasm.FunctionType{{Results: params, Params: params}},
  1341  		ExportSection:   []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}},
  1342  		FunctionSection: []wasm.Index{0},
  1343  		CodeSection:     []wasm.Code{{Body: body}},
  1344  	})
  1345  
  1346  	var buf bytes.Buffer
  1347  	config := wazero.NewRuntimeConfigCompiler()
  1348  	ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf))
  1349  
  1350  	r := wazero.NewRuntimeWithConfig(ctx, config)
  1351  	defer func() {
  1352  		require.NoError(t, r.Close(ctx))
  1353  	}()
  1354  
  1355  	inst, err := r.Instantiate(ctx, bin)
  1356  	require.NoError(t, err)
  1357  
  1358  	f := inst.ExportedFunction("main")
  1359  	require.NotNil(t, f)
  1360  	param := make([]uint64, paramNum)
  1361  	for i := range param {
  1362  		param[i] = uint64(i)
  1363  	}
  1364  	res, err := f.Call(ctx, param...)
  1365  	require.NoError(t, err)
  1366  	require.Equal(t, param, res)
  1367  
  1368  	require.Equal(t, `
  1369  --> .$0(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19)
  1370  <-- (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19)
  1371  `, "\n"+buf.String())
  1372  }
  1373  
  1374  func TestListener_long_many_consts(t *testing.T) {
  1375  	const paramNum = 61
  1376  
  1377  	var exp []uint64
  1378  	var body []byte
  1379  	var resultTypes []wasm.ValueType
  1380  	for i := 0; i < paramNum; i++ {
  1381  		exp = append(exp, uint64(i))
  1382  		resultTypes = append(resultTypes, i32)
  1383  		body = append(body, wasm.OpcodeI32Const)
  1384  		body = append(body, leb128.EncodeInt32(int32(i))...)
  1385  	}
  1386  	body = append(body, wasm.OpcodeEnd)
  1387  
  1388  	bin := binaryencoding.EncodeModule(&wasm.Module{
  1389  		TypeSection:     []wasm.FunctionType{{Results: resultTypes}},
  1390  		ExportSection:   []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}},
  1391  		FunctionSection: []wasm.Index{0},
  1392  		CodeSection:     []wasm.Code{{Body: body}},
  1393  	})
  1394  
  1395  	var buf bytes.Buffer
  1396  	config := wazero.NewRuntimeConfigCompiler()
  1397  	ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf))
  1398  
  1399  	r := wazero.NewRuntimeWithConfig(ctx, config)
  1400  	defer func() {
  1401  		require.NoError(t, r.Close(ctx))
  1402  	}()
  1403  
  1404  	inst, err := r.Instantiate(ctx, bin)
  1405  	require.NoError(t, err)
  1406  
  1407  	f := inst.ExportedFunction("main")
  1408  	require.NotNil(t, f)
  1409  	res, err := f.Call(ctx)
  1410  	require.NoError(t, err)
  1411  	require.Equal(t, exp, res)
  1412  
  1413  	require.Equal(t, `
  1414  --> .$0()
  1415  <-- (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60)
  1416  `, "\n"+buf.String())
  1417  }
  1418  
  1419  // TestDWARF verifies that the DWARF based stack traces work as expected before/after compilation cache.
  1420  func TestDWARF(t *testing.T) {
  1421  	config := wazero.NewRuntimeConfigCompiler()
  1422  	ctx := context.Background()
  1423  
  1424  	bin := dwarftestdata.ZigWasm
  1425  
  1426  	dir := t.TempDir()
  1427  
  1428  	var expErr error
  1429  	{
  1430  		cc, err := wazero.NewCompilationCacheWithDir(dir)
  1431  		require.NoError(t, err)
  1432  		rc := config.WithCompilationCache(cc)
  1433  
  1434  		r := wazero.NewRuntimeWithConfig(ctx, rc)
  1435  		_, expErr = r.Instantiate(ctx, bin)
  1436  		require.Error(t, expErr)
  1437  
  1438  		err = r.Close(ctx)
  1439  		require.NoError(t, err)
  1440  	}
  1441  
  1442  	cc, err := wazero.NewCompilationCacheWithDir(dir)
  1443  	require.NoError(t, err)
  1444  	rc := config.WithCompilationCache(cc)
  1445  	r := wazero.NewRuntimeWithConfig(ctx, rc)
  1446  	_, err = r.Instantiate(ctx, bin)
  1447  	require.Error(t, err)
  1448  	require.Equal(t, expErr.Error(), err.Error())
  1449  
  1450  	err = r.Close(ctx)
  1451  	require.NoError(t, err)
  1452  }