github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/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  	wazero "github.com/wasilibs/wazerox"
    12  	"github.com/wasilibs/wazerox/api"
    13  	"github.com/wasilibs/wazerox/experimental"
    14  	"github.com/wasilibs/wazerox/experimental/logging"
    15  	"github.com/wasilibs/wazerox/experimental/opt"
    16  	"github.com/wasilibs/wazerox/internal/engine/wazevo/testcases"
    17  	"github.com/wasilibs/wazerox/internal/leb128"
    18  	"github.com/wasilibs/wazerox/internal/testing/binaryencoding"
    19  	"github.com/wasilibs/wazerox/internal/testing/dwarftestdata"
    20  	"github.com/wasilibs/wazerox/internal/testing/require"
    21  	"github.com/wasilibs/wazerox/internal/wasm"
    22  )
    23  
    24  const (
    25  	i32  = wasm.ValueTypeI32
    26  	i64  = wasm.ValueTypeI64
    27  	f32  = wasm.ValueTypeF32
    28  	f64  = wasm.ValueTypeF64
    29  	v128 = wasm.ValueTypeV128
    30  )
    31  
    32  func TestE2E(t *testing.T) {
    33  	tmp := t.TempDir()
    34  	type callCase struct {
    35  		funcName           string // defaults to testcases.ExportedFunctionName
    36  		params, expResults []uint64
    37  		expErr             string
    38  	}
    39  	for _, tc := range []struct {
    40  		name        string
    41  		imported, m *wasm.Module
    42  		calls       []callCase
    43  	}{
    44  		{
    45  			name: "selects", m: testcases.Selects.Module,
    46  			calls: []callCase{
    47  				{
    48  					params: []uint64{
    49  						0, 1, // i32,
    50  						200, 100, // i64,
    51  						uint64(math.Float32bits(3.0)), uint64(math.Float32bits(10.0)),
    52  						math.Float64bits(-123.4), math.Float64bits(-10000000000.0),
    53  					},
    54  					expResults: []uint64{
    55  						1,
    56  						200,
    57  						uint64(math.Float32bits(3.0)),
    58  						math.Float64bits(-123.4),
    59  					},
    60  				},
    61  			},
    62  		},
    63  		{
    64  			name: "swap", m: testcases.SwapParamAndReturn.Module,
    65  			calls: []callCase{
    66  				{params: []uint64{math.MaxUint32, math.MaxInt32}, expResults: []uint64{math.MaxInt32, math.MaxUint32}},
    67  			},
    68  		},
    69  		{
    70  			name: "consts", m: testcases.Constants.Module,
    71  			calls: []callCase{
    72  				{expResults: []uint64{1, 2, uint64(math.Float32bits(32.0)), math.Float64bits(64.0)}},
    73  			},
    74  		},
    75  		{
    76  			name: "unreachable", m: testcases.Unreachable.Module,
    77  			calls: []callCase{{expErr: "unreachable"}},
    78  		},
    79  		{
    80  			name: "fibonacci_recursive", m: testcases.FibonacciRecursive.Module,
    81  			calls: []callCase{
    82  				{params: []uint64{0}, expResults: []uint64{0}},
    83  				{params: []uint64{1}, expResults: []uint64{1}},
    84  				{params: []uint64{2}, expResults: []uint64{1}},
    85  				{params: []uint64{10}, expResults: []uint64{55}},
    86  				{params: []uint64{20}, expResults: []uint64{6765}},
    87  				{params: []uint64{30}, expResults: []uint64{0xcb228}},
    88  			},
    89  		},
    90  		{name: "call", m: testcases.Call.Module, calls: []callCase{{expResults: []uint64{45, 45}}}},
    91  		{
    92  			name: "stack overflow",
    93  			m: &wasm.Module{
    94  				TypeSection:     []wasm.FunctionType{{}},
    95  				FunctionSection: []wasm.Index{0},
    96  				CodeSection:     []wasm.Code{{Body: []byte{wasm.OpcodeCall, 0, wasm.OpcodeEnd}}},
    97  				ExportSection:   []wasm.Export{{Name: testcases.ExportedFunctionName, Index: 0, Type: wasm.ExternTypeFunc}},
    98  			},
    99  			calls: []callCase{
   100  				{expErr: "stack overflow"}, {expErr: "stack overflow"}, {expErr: "stack overflow"}, {expErr: "stack overflow"},
   101  			},
   102  		},
   103  		{
   104  			name:     "imported_function_call",
   105  			imported: testcases.ImportedFunctionCall.Imported,
   106  			m:        testcases.ImportedFunctionCall.Module,
   107  			calls: []callCase{
   108  				{params: []uint64{0}, expResults: []uint64{0}},
   109  				{params: []uint64{2}, expResults: []uint64{2 * 2}},
   110  				{params: []uint64{45}, expResults: []uint64{45 * 45}},
   111  				{params: []uint64{90}, expResults: []uint64{90 * 90}},
   112  				{params: []uint64{100}, expResults: []uint64{100 * 100}},
   113  				{params: []uint64{100, 200}, expResults: []uint64{100 * 200}, funcName: "imported_exported"},
   114  			},
   115  		},
   116  		{
   117  			name: "memory_store_basic",
   118  			m:    testcases.MemoryStoreBasic.Module,
   119  			calls: []callCase{
   120  				{params: []uint64{0, 0xf}, expResults: []uint64{0xf}},
   121  				{params: []uint64{256, 0xff}, expResults: []uint64{0xff}},
   122  				{params: []uint64{100, 0xffffffff}, expResults: []uint64{0xffffffff}},
   123  				// We load I32, so we can't load from the last 3 bytes.
   124  				{params: []uint64{uint64(wasm.MemoryPageSize) - 3, 0}, expErr: "out of bounds memory access"},
   125  			},
   126  		},
   127  		{
   128  			name: "memory_load_basic",
   129  			m:    testcases.MemoryLoadBasic.Module,
   130  			calls: []callCase{
   131  				{params: []uint64{0}, expResults: []uint64{0x03_02_01_00}},
   132  				{params: []uint64{256}, expResults: []uint64{0x03_02_01_00}},
   133  				{params: []uint64{100}, expResults: []uint64{103<<24 | 102<<16 | 101<<8 | 100}},
   134  				// Last 4 bytes.
   135  				{params: []uint64{uint64(wasm.MemoryPageSize) - 4}, expResults: []uint64{0xfffefdfc}},
   136  			},
   137  		},
   138  		{
   139  			name: "memory out of bounds",
   140  			m:    testcases.MemoryLoadBasic.Module,
   141  			calls: []callCase{
   142  				{params: []uint64{uint64(wasm.MemoryPageSize)}, expErr: "out of bounds memory access"},
   143  				// We load I32, so we can't load from the last 3 bytes.
   144  				{params: []uint64{uint64(wasm.MemoryPageSize) - 3}, expErr: "out of bounds memory access"},
   145  			},
   146  		},
   147  		{
   148  			name: "memory_loads",
   149  			m:    testcases.MemoryLoads.Module,
   150  			calls: []callCase{
   151  				// These expected results are derived by commenting out `configureWazevo(config)` below to run the old compiler, assuming that it is correct.
   152  				{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}},
   153  				{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}},
   154  				{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}},
   155  				{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}},
   156  				{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}},
   157  				{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}},
   158  				{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}},
   159  				{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}},
   160  			},
   161  		},
   162  		{
   163  			name: "globals_get",
   164  			m:    testcases.GlobalsGet.Module,
   165  			calls: []callCase{
   166  				{expResults: []uint64{0x80000000, 0x8000000000000000, 0x7f7fffff, 0x7fefffffffffffff}},
   167  			},
   168  		},
   169  		{
   170  			name:  "globals_set",
   171  			m:     testcases.GlobalsSet.Module,
   172  			calls: []callCase{{expResults: []uint64{1, 2, uint64(math.Float32bits(3.0)), math.Float64bits(4.0)}}},
   173  		},
   174  		{
   175  			name: "globals_mutable",
   176  			m:    testcases.GlobalsMutable.Module,
   177  			calls: []callCase{{expResults: []uint64{
   178  				100, 200, uint64(math.Float32bits(300.0)), math.Float64bits(400.0),
   179  				1, 2, uint64(math.Float32bits(3.0)), math.Float64bits(4.0),
   180  			}}},
   181  		},
   182  		{
   183  			name:  "memory_size_grow",
   184  			m:     testcases.MemorySizeGrow.Module,
   185  			calls: []callCase{{expResults: []uint64{1, 2, 0xffffffff}}},
   186  		},
   187  		{
   188  			name:     "imported_memory_grow",
   189  			imported: testcases.ImportedMemoryGrow.Imported,
   190  			m:        testcases.ImportedMemoryGrow.Module,
   191  			calls:    []callCase{{expResults: []uint64{1, 1, 11, 11}}},
   192  		},
   193  		{
   194  			name: "call_indirect",
   195  			m:    testcases.CallIndirect.Module,
   196  			// parameter == table offset.
   197  			calls: []callCase{
   198  				{params: []uint64{0}, expErr: "indirect call type mismatch"},
   199  				{params: []uint64{1}, expResults: []uint64{10}},
   200  				{params: []uint64{2}, expErr: "indirect call type mismatch"},
   201  				{params: []uint64{10}, expErr: "invalid table access"},             // Null pointer.
   202  				{params: []uint64{math.MaxUint32}, expErr: "invalid table access"}, // Out of bounds.
   203  			},
   204  		},
   205  		{
   206  			name: "br_table",
   207  			m:    testcases.BrTable.Module,
   208  			calls: []callCase{
   209  				{params: []uint64{0}, expResults: []uint64{11}},
   210  				{params: []uint64{1}, expResults: []uint64{12}},
   211  				{params: []uint64{2}, expResults: []uint64{13}},
   212  				{params: []uint64{3}, expResults: []uint64{14}},
   213  				{params: []uint64{4}, expResults: []uint64{15}},
   214  				{params: []uint64{5}, expResults: []uint64{16}},
   215  				// Out of range --> default.
   216  				{params: []uint64{6}, expResults: []uint64{11}},
   217  				{params: []uint64{1000}, expResults: []uint64{11}},
   218  			},
   219  		},
   220  		{
   221  			name: "br_table_with_args",
   222  			m:    testcases.BrTableWithArg.Module,
   223  			calls: []callCase{
   224  				{params: []uint64{0, 100}, expResults: []uint64{11 + 100}},
   225  				{params: []uint64{1, 100}, expResults: []uint64{12 + 100}},
   226  				{params: []uint64{2, 100}, expResults: []uint64{13 + 100}},
   227  				{params: []uint64{3, 100}, expResults: []uint64{14 + 100}},
   228  				{params: []uint64{4, 100}, expResults: []uint64{15 + 100}},
   229  				{params: []uint64{5, 100}, expResults: []uint64{16 + 100}},
   230  				// Out of range --> default.
   231  				{params: []uint64{6, 200}, expResults: []uint64{11 + 200}},
   232  				{params: []uint64{1000, 300}, expResults: []uint64{11 + 300}},
   233  			},
   234  		},
   235  		{
   236  			name: "multi_predecessor_local_ref",
   237  			m:    testcases.MultiPredecessorLocalRef.Module,
   238  			calls: []callCase{
   239  				{params: []uint64{0, 100}, expResults: []uint64{100}},
   240  				{params: []uint64{1, 100}, expResults: []uint64{1}},
   241  				{params: []uint64{1, 200}, expResults: []uint64{1}},
   242  			},
   243  		},
   244  		{
   245  			name: "vector_bit_select",
   246  			m:    testcases.VecBitSelect.Module,
   247  			calls: []callCase{
   248  				{params: []uint64{1, 2, 3, 4, 5, 6}, expResults: []uint64{0x3, 0x2, 0x5, 0x6}},
   249  			},
   250  		},
   251  		{
   252  			name: "vector_shuffle",
   253  			m:    testcases.VecShuffle.Module,
   254  			calls: []callCase{
   255  				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x01010101, 0x04040404}},
   256  				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x03030303, 0x02020202}},
   257  				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0x00000000, 0x00000000}},
   258  				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xffffffff, 0xffffffff}},
   259  				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x00000000, 0xffffffff}},
   260  			},
   261  		},
   262  		{
   263  			name: "vector_shuffle (1st only)",
   264  			m:    testcases.VecShuffleWithLane(1, 1, 1, 1, 0, 0, 0, 0, 10, 10, 10, 10, 0, 0, 0, 0),
   265  			calls: []callCase{
   266  				{params: []uint64{0x0000000000000b0a, 0x0c0000, 0xffffffffffffffff, 0xffffffffffffffff}, expResults: []uint64{0x0a0a0a0a0b0b0b0b, 0x0a0a0a0a0c0c0c0c}},
   267  				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x0101010101010101, 0x101010102020202}},
   268  				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x0303030303030303, 0x303030304040404}},
   269  				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0x0000000000000000, 0x0000000ffffffff}},
   270  				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xffffffffffffffff, 0xffffffff00000000}},
   271  				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x0000000000000000, 0x0000000011111111}},
   272  			},
   273  		},
   274  		{
   275  			name: "vector_shuffle (2nd only)",
   276  			m:    testcases.VecShuffleWithLane(17, 17, 17, 17, 16, 16, 16, 16, 26, 26, 26, 26, 16, 16, 16, 16),
   277  			calls: []callCase{
   278  				{params: []uint64{0xffffffffffffffff, 0xffffffffffffffff, 0x0000000000000b0a, 0x0c0000}, expResults: []uint64{0x0a0a0a0a0b0b0b0b, 0x0a0a0a0a0c0c0c0c}},
   279  				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x303030303030303, 0x303030304040404}},
   280  				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x101010101010101, 0x101010102020202}},
   281  				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0xffffffffffffffff, 0xffffffff00000000}},
   282  				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0x0000000000000000, 0xffffffff}},
   283  				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x1111111111111111, 0x11111111ffffffff}},
   284  			},
   285  		},
   286  		{
   287  			name: "vector_shuffle (mixed)",
   288  			m:    testcases.VecShuffleWithLane(0, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31),
   289  			calls: []callCase{
   290  				{params: []uint64{0xff08ff07ff06ff05, 0xff04ff03ff02ff01, 0x18ff17ff16ff15ff, 0x14ff13ff12ff11ff}, expResults: []uint64{0x1808170716061505, 0x1404130312021101}},
   291  				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x3010301, 0x4020402}},
   292  				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x1030103, 0x2040204}},
   293  				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0xff00ff00, 0xff00ff}},
   294  				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xff00ff, 0xff00ff00}},
   295  				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x11001100, 0xff11ff11}},
   296  			},
   297  		},
   298  	} {
   299  		tc := tc
   300  		t.Run(tc.name, func(t *testing.T) {
   301  			for i := 0; i < 2; i++ {
   302  				var name string
   303  				if i == 0 {
   304  					name = "no cache"
   305  				} else {
   306  					name = "with cache"
   307  				}
   308  				t.Run(name, func(t *testing.T) {
   309  					cache, err := wazero.NewCompilationCacheWithDir(tmp)
   310  					require.NoError(t, err)
   311  					config := opt.NewRuntimeConfigOptimizingCompiler().WithCompilationCache(cache)
   312  
   313  					ctx := context.Background()
   314  					r := wazero.NewRuntimeWithConfig(ctx, config)
   315  					defer func() {
   316  						require.NoError(t, r.Close(ctx))
   317  					}()
   318  
   319  					if tc.imported != nil {
   320  						imported, err := r.CompileModule(ctx, binaryencoding.EncodeModule(tc.imported))
   321  						require.NoError(t, err)
   322  
   323  						_, err = r.InstantiateModule(ctx, imported, wazero.NewModuleConfig())
   324  						require.NoError(t, err)
   325  					}
   326  
   327  					compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(tc.m))
   328  					require.NoError(t, err)
   329  
   330  					inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
   331  					require.NoError(t, err)
   332  
   333  					for _, cc := range tc.calls {
   334  						name := cc.funcName
   335  						if name == "" {
   336  							name = testcases.ExportedFunctionName
   337  						}
   338  						t.Run(fmt.Sprintf("call_%s%v", name, cc.params), func(t *testing.T) {
   339  							f := inst.ExportedFunction(name)
   340  							require.NotNil(t, f)
   341  							result, err := f.Call(ctx, cc.params...)
   342  							if cc.expErr != "" {
   343  								require.Contains(t, err.Error(), cc.expErr)
   344  							} else {
   345  								require.NoError(t, err)
   346  								require.Equal(t, len(cc.expResults), len(result))
   347  								require.Equal(t, cc.expResults, result)
   348  								for i := range cc.expResults {
   349  									if cc.expResults[i] != result[i] {
   350  										t.Errorf("result[%d]: exp %d, got %d", i, cc.expResults[i], result[i])
   351  									}
   352  								}
   353  							}
   354  						})
   355  					}
   356  				})
   357  			}
   358  		})
   359  	}
   360  }
   361  
   362  func TestE2E_host_functions(t *testing.T) {
   363  	var buf bytes.Buffer
   364  	ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&buf))
   365  
   366  	for _, tc := range []struct {
   367  		name string
   368  		ctx  context.Context
   369  	}{
   370  		{name: "listener", ctx: ctx},
   371  		{name: "no listener", ctx: context.Background()},
   372  	} {
   373  		tc := tc
   374  		t.Run(tc.name, func(t *testing.T) {
   375  			ctx := tc.ctx
   376  
   377  			config := opt.NewRuntimeConfigOptimizingCompiler()
   378  
   379  			r := wazero.NewRuntimeWithConfig(ctx, config)
   380  			defer func() {
   381  				require.NoError(t, r.Close(ctx))
   382  			}()
   383  
   384  			var expectedMod api.Module
   385  
   386  			b := r.NewHostModuleBuilder("env")
   387  			b.NewFunctionBuilder().WithFunc(func(ctx2 context.Context, d float64) float64 {
   388  				require.Equal(t, ctx, ctx2)
   389  				require.Equal(t, 35.0, d)
   390  				return math.Sqrt(d)
   391  			}).Export("root")
   392  			b.NewFunctionBuilder().WithFunc(func(ctx2 context.Context, mod api.Module, a uint32, b uint64, c float32, d float64) (uint32, uint64, float32, float64) {
   393  				require.Equal(t, expectedMod, mod)
   394  				require.Equal(t, ctx, ctx2)
   395  				require.Equal(t, uint32(2), a)
   396  				require.Equal(t, uint64(100), b)
   397  				require.Equal(t, float32(15.0), c)
   398  				require.Equal(t, 35.0, d)
   399  				return a * a, b * b, c * c, d * d
   400  			}).Export("square")
   401  
   402  			_, err := b.Instantiate(ctx)
   403  			require.NoError(t, err)
   404  
   405  			m := &wasm.Module{
   406  				ImportFunctionCount: 2,
   407  				ImportSection: []wasm.Import{
   408  					{Module: "env", Name: "root", Type: wasm.ExternTypeFunc, DescFunc: 0},
   409  					{Module: "env", Name: "square", Type: wasm.ExternTypeFunc, DescFunc: 1},
   410  				},
   411  				TypeSection: []wasm.FunctionType{
   412  					{Results: []wasm.ValueType{f64}, Params: []wasm.ValueType{f64}},
   413  					{Results: []wasm.ValueType{i32, i64, f32, f64}, Params: []wasm.ValueType{i32, i64, f32, f64}},
   414  					{Results: []wasm.ValueType{i32, i64, f32, f64, f64}, Params: []wasm.ValueType{i32, i64, f32, f64}},
   415  				},
   416  				FunctionSection: []wasm.Index{2},
   417  				CodeSection: []wasm.Code{{
   418  					Body: []byte{
   419  						wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 1, wasm.OpcodeLocalGet, 2, wasm.OpcodeLocalGet, 3,
   420  						wasm.OpcodeCall, 1,
   421  						wasm.OpcodeLocalGet, 3,
   422  						wasm.OpcodeCall, 0,
   423  						wasm.OpcodeEnd,
   424  					},
   425  				}},
   426  				ExportSection: []wasm.Export{{Name: testcases.ExportedFunctionName, Type: wasm.ExternTypeFunc, Index: 2}},
   427  			}
   428  
   429  			compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(m))
   430  			require.NoError(t, err)
   431  
   432  			inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
   433  			require.NoError(t, err)
   434  
   435  			expectedMod = inst
   436  
   437  			f := inst.ExportedFunction(testcases.ExportedFunctionName)
   438  
   439  			res, err := f.Call(ctx, []uint64{2, 100, uint64(math.Float32bits(15.0)), math.Float64bits(35.0)}...)
   440  			require.NoError(t, err)
   441  			require.Equal(t, []uint64{
   442  				2 * 2, 100 * 100, uint64(math.Float32bits(15.0 * 15.0)), math.Float64bits(35.0 * 35.0),
   443  				math.Float64bits(math.Sqrt(35.0)),
   444  			}, res)
   445  		})
   446  	}
   447  
   448  	require.Equal(t, `
   449  --> .$2(2,100,15,35)
   450  	==> env.square(2,100,15,35)
   451  	<== (4,10000,225,1225)
   452  	==> env.root(35)
   453  	<== 5.916079783099616
   454  <-- (4,10000,225,1225,5.916079783099616)
   455  `, "\n"+buf.String())
   456  }
   457  
   458  func TestE2E_stores(t *testing.T) {
   459  	config := opt.NewRuntimeConfigOptimizingCompiler()
   460  
   461  	ctx := context.Background()
   462  	r := wazero.NewRuntimeWithConfig(ctx, config)
   463  	defer func() {
   464  		require.NoError(t, r.Close(ctx))
   465  	}()
   466  
   467  	compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(testcases.MemoryStores.Module))
   468  	require.NoError(t, err)
   469  
   470  	inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
   471  	require.NoError(t, err)
   472  
   473  	f := inst.ExportedFunction(testcases.ExportedFunctionName)
   474  
   475  	mem, ok := inst.Memory().Read(0, wasm.MemoryPageSize)
   476  	require.True(t, ok)
   477  	for _, tc := range []struct {
   478  		i32 uint32
   479  		i64 uint64
   480  		f32 float32
   481  		f64 float64
   482  	}{
   483  		{0, 0, 0, 0},
   484  		{1, 2, 3.0, 4.0},
   485  		{math.MaxUint32, math.MaxUint64, float32(math.NaN()), math.NaN()},
   486  		{1 << 31, 1 << 63, 3.0, 4.0},
   487  	} {
   488  		t.Run(fmt.Sprintf("i32=%#x,i64=%#x,f32=%#x,f64=%#x", tc.i32, tc.i64, tc.f32, tc.f64), func(t *testing.T) {
   489  			_, err = f.Call(ctx, []uint64{uint64(tc.i32), tc.i64, uint64(math.Float32bits(tc.f32)), math.Float64bits(tc.f64)}...)
   490  			require.NoError(t, err)
   491  
   492  			offset := 0
   493  			require.Equal(t, binary.LittleEndian.Uint32(mem[offset:]), tc.i32)
   494  			offset += 8
   495  			require.Equal(t, binary.LittleEndian.Uint64(mem[offset:]), tc.i64)
   496  			offset += 8
   497  			require.Equal(t, math.Float32bits(tc.f32), binary.LittleEndian.Uint32(mem[offset:]))
   498  			offset += 8
   499  			require.Equal(t, math.Float64bits(tc.f64), binary.LittleEndian.Uint64(mem[offset:]))
   500  			offset += 8
   501  
   502  			// i32.store_8
   503  			view := binary.LittleEndian.Uint64(mem[offset:])
   504  			require.Equal(t, uint64(tc.i32)&0xff, view)
   505  			offset += 8
   506  			// i32.store_16
   507  			view = binary.LittleEndian.Uint64(mem[offset:])
   508  			require.Equal(t, uint64(tc.i32)&0xffff, view)
   509  			offset += 8
   510  			// i64.store_8
   511  			view = binary.LittleEndian.Uint64(mem[offset:])
   512  			require.Equal(t, tc.i64&0xff, view)
   513  			offset += 8
   514  			// i64.store_16
   515  			view = binary.LittleEndian.Uint64(mem[offset:])
   516  			require.Equal(t, tc.i64&0xffff, view)
   517  			offset += 8
   518  			// i64.store_32
   519  			view = binary.LittleEndian.Uint64(mem[offset:])
   520  			require.Equal(t, tc.i64&0xffffffff, view)
   521  		})
   522  	}
   523  }
   524  
   525  func TestE2E_reexported_memory(t *testing.T) {
   526  	m1 := &wasm.Module{
   527  		ExportSection: []wasm.Export{{Name: "mem", Type: wasm.ExternTypeMemory, Index: 0}},
   528  		MemorySection: &wasm.Memory{Min: 1},
   529  		NameSection:   &wasm.NameSection{ModuleName: "m1"},
   530  	}
   531  	m2 := &wasm.Module{
   532  		ImportMemoryCount: 1,
   533  		ExportSection:     []wasm.Export{{Name: "mem2", Type: wasm.ExternTypeMemory, Index: 0}},
   534  		ImportSection:     []wasm.Import{{Module: "m1", Name: "mem", Type: wasm.ExternTypeMemory, DescMem: &wasm.Memory{Min: 1}}},
   535  		NameSection:       &wasm.NameSection{ModuleName: "m2"},
   536  	}
   537  	m3 := &wasm.Module{
   538  		ImportMemoryCount: 1,
   539  		ImportSection:     []wasm.Import{{Module: "m2", Name: "mem2", Type: wasm.ExternTypeMemory, DescMem: &wasm.Memory{Min: 1}}},
   540  		TypeSection:       []wasm.FunctionType{{Results: []wasm.ValueType{i32}}},
   541  		ExportSection:     []wasm.Export{{Name: testcases.ExportedFunctionName, Type: wasm.ExternTypeFunc, Index: 0}},
   542  		FunctionSection:   []wasm.Index{0},
   543  		CodeSection:       []wasm.Code{{Body: []byte{wasm.OpcodeI32Const, 10, wasm.OpcodeMemoryGrow, 0, wasm.OpcodeEnd}}},
   544  	}
   545  
   546  	config := opt.NewRuntimeConfigOptimizingCompiler()
   547  
   548  	ctx := context.Background()
   549  	r := wazero.NewRuntimeWithConfig(ctx, config)
   550  	defer func() {
   551  		require.NoError(t, r.Close(ctx))
   552  	}()
   553  
   554  	m1Inst, err := r.Instantiate(ctx, binaryencoding.EncodeModule(m1))
   555  	require.NoError(t, err)
   556  
   557  	m2Inst, err := r.Instantiate(ctx, binaryencoding.EncodeModule(m2))
   558  	require.NoError(t, err)
   559  
   560  	m3Inst, err := r.Instantiate(ctx, binaryencoding.EncodeModule(m3))
   561  	require.NoError(t, err)
   562  
   563  	f := m3Inst.ExportedFunction(testcases.ExportedFunctionName)
   564  	result, err := f.Call(ctx)
   565  	require.NoError(t, err)
   566  	require.Equal(t, uint64(1), result[0])
   567  	mem := m1Inst.Memory()
   568  	require.Equal(t, mem, m3Inst.Memory())
   569  	require.Equal(t, mem, m2Inst.Memory())
   570  	require.Equal(t, uint32(11), mem.Size()/65536)
   571  }
   572  
   573  func TestStackUnwind_panic_in_host(t *testing.T) {
   574  	unreachable := &wasm.Module{
   575  		ImportFunctionCount: 1,
   576  		ImportSection:       []wasm.Import{{Module: "host", Name: "cause_unreachable", Type: wasm.ExternTypeFunc, DescFunc: 0}},
   577  		TypeSection:         []wasm.FunctionType{{}},
   578  		ExportSection:       []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 1}},
   579  		FunctionSection:     []wasm.Index{0, 0, 0},
   580  		CodeSection: []wasm.Code{
   581  			{Body: []byte{wasm.OpcodeCall, 2, wasm.OpcodeEnd}},
   582  			{Body: []byte{wasm.OpcodeCall, 3, wasm.OpcodeEnd}},
   583  			{Body: []byte{wasm.OpcodeCall, 0, wasm.OpcodeEnd}}, // call host.cause_unreachable.
   584  		},
   585  		NameSection: &wasm.NameSection{
   586  			FunctionNames: wasm.NameMap{
   587  				wasm.NameAssoc{Index: 0, Name: "host.unreachable"},
   588  				wasm.NameAssoc{Index: 1, Name: "main"},
   589  				wasm.NameAssoc{Index: 2, Name: "one"},
   590  				wasm.NameAssoc{Index: 3, Name: "two"},
   591  			},
   592  		},
   593  	}
   594  
   595  	config := opt.NewRuntimeConfigOptimizingCompiler()
   596  
   597  	ctx := context.Background()
   598  	r := wazero.NewRuntimeWithConfig(ctx, config)
   599  	defer func() {
   600  		require.NoError(t, r.Close(ctx))
   601  	}()
   602  
   603  	callUnreachable := func() {
   604  		panic("panic in host function")
   605  	}
   606  
   607  	_, err := r.NewHostModuleBuilder("host").
   608  		NewFunctionBuilder().WithFunc(callUnreachable).Export("cause_unreachable").
   609  		Instantiate(ctx)
   610  	require.NoError(t, err)
   611  
   612  	module, err := r.Instantiate(ctx, binaryencoding.EncodeModule(unreachable))
   613  	require.NoError(t, err)
   614  	defer module.Close(ctx)
   615  
   616  	_, err = module.ExportedFunction("main").Call(ctx)
   617  	exp := `panic in host function (recovered by wazero)
   618  wasm stack trace:
   619  	host.cause_unreachable()
   620  	.two()
   621  	.one()
   622  	.main()`
   623  	require.Equal(t, exp, err.Error())
   624  }
   625  
   626  func TestStackUnwind_unreachable(t *testing.T) {
   627  	unreachable := &wasm.Module{
   628  		TypeSection:     []wasm.FunctionType{{}},
   629  		ExportSection:   []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}},
   630  		FunctionSection: []wasm.Index{0, 0, 0},
   631  		CodeSection: []wasm.Code{
   632  			{Body: []byte{wasm.OpcodeCall, 1, wasm.OpcodeEnd}},
   633  			{Body: []byte{wasm.OpcodeCall, 2, wasm.OpcodeEnd}},
   634  			{Body: []byte{wasm.OpcodeUnreachable, wasm.OpcodeEnd}},
   635  		},
   636  		NameSection: &wasm.NameSection{
   637  			FunctionNames: wasm.NameMap{
   638  				wasm.NameAssoc{Index: 0, Name: "main"},
   639  				wasm.NameAssoc{Index: 1, Name: "one"},
   640  				wasm.NameAssoc{Index: 2, Name: "two"},
   641  			},
   642  		},
   643  	}
   644  
   645  	config := opt.NewRuntimeConfigOptimizingCompiler()
   646  	ctx := context.Background()
   647  	r := wazero.NewRuntimeWithConfig(ctx, config)
   648  	defer func() {
   649  		require.NoError(t, r.Close(ctx))
   650  	}()
   651  
   652  	module, err := r.Instantiate(ctx, binaryencoding.EncodeModule(unreachable))
   653  	require.NoError(t, err)
   654  	defer module.Close(ctx)
   655  
   656  	_, err = module.ExportedFunction("main").Call(ctx)
   657  	exp := `wasm error: unreachable
   658  wasm stack trace:
   659  	.two()
   660  	.one()
   661  	.main()`
   662  	require.Equal(t, exp, err.Error())
   663  }
   664  
   665  func TestListener_local(t *testing.T) {
   666  	var buf bytes.Buffer
   667  	config := opt.NewRuntimeConfigOptimizingCompiler()
   668  	ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&buf))
   669  
   670  	r := wazero.NewRuntimeWithConfig(ctx, config)
   671  	defer func() {
   672  		require.NoError(t, r.Close(ctx))
   673  	}()
   674  
   675  	compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(testcases.CallIndirect.Module))
   676  	require.NoError(t, err)
   677  
   678  	inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
   679  	require.NoError(t, err)
   680  
   681  	res, err := inst.ExportedFunction(testcases.ExportedFunctionName).Call(ctx, 1)
   682  	require.NoError(t, err)
   683  	require.Equal(t, []uint64{10}, res)
   684  
   685  	require.Equal(t, `
   686  --> .$0(1)
   687  	--> .$2()
   688  	<-- 10
   689  <-- 10
   690  `, "\n"+buf.String())
   691  }
   692  
   693  func TestListener_imported(t *testing.T) {
   694  	var buf bytes.Buffer
   695  	config := opt.NewRuntimeConfigOptimizingCompiler()
   696  	ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&buf))
   697  
   698  	r := wazero.NewRuntimeWithConfig(ctx, config)
   699  	defer func() {
   700  		require.NoError(t, r.Close(ctx))
   701  	}()
   702  
   703  	_, err := r.Instantiate(ctx, binaryencoding.EncodeModule(testcases.ImportedFunctionCall.Imported))
   704  	require.NoError(t, err)
   705  
   706  	compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(testcases.ImportedFunctionCall.Module))
   707  	require.NoError(t, err)
   708  
   709  	inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
   710  	require.NoError(t, err)
   711  
   712  	res, err := inst.ExportedFunction(testcases.ExportedFunctionName).Call(ctx, 100)
   713  	require.NoError(t, err)
   714  	require.Equal(t, []uint64{10000}, res)
   715  
   716  	require.Equal(t, `
   717  --> .$1(100)
   718  	--> env.$0(100,100)
   719  	<-- 10000
   720  <-- 10000
   721  `, "\n"+buf.String())
   722  }
   723  
   724  func TestListener_long(t *testing.T) {
   725  	pickOneParam := binaryencoding.EncodeModule(&wasm.Module{
   726  		TypeSection: []wasm.FunctionType{{Results: []wasm.ValueType{i32}, Params: []wasm.ValueType{
   727  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   728  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   729  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   730  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   731  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   732  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   733  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   734  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   735  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   736  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   737  		}}},
   738  		ExportSection:   []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}},
   739  		FunctionSection: []wasm.Index{0},
   740  		CodeSection: []wasm.Code{
   741  			{Body: []byte{wasm.OpcodeLocalGet, 10, wasm.OpcodeEnd}},
   742  		},
   743  	})
   744  
   745  	var buf bytes.Buffer
   746  	config := opt.NewRuntimeConfigOptimizingCompiler()
   747  	ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&buf))
   748  
   749  	r := wazero.NewRuntimeWithConfig(ctx, config)
   750  	defer func() {
   751  		require.NoError(t, r.Close(ctx))
   752  	}()
   753  
   754  	inst, err := r.Instantiate(ctx, pickOneParam)
   755  	require.NoError(t, err)
   756  
   757  	f := inst.ExportedFunction("main")
   758  	require.NotNil(t, f)
   759  	param := make([]uint64, 100)
   760  	for i := range param {
   761  		param[i] = uint64(i)
   762  	}
   763  	res, err := f.Call(ctx, param...)
   764  	require.NoError(t, err)
   765  	require.Equal(t, []uint64{0xb}, res)
   766  
   767  	require.Equal(t, `
   768  --> .$0(0,1,3e-45,1.5e-323,4,5,6,00000000000000070000000000000008,1.1e-44,9,10,1.5e-44,6e-323,13,14,15,00000000000000100000000000000011,2.4e-44,18,19,2.8e-44,1.04e-322,22,23,24,0000000000000019000000000000001a,3.6e-44,27,28,4e-44,1.5e-322,31,32,33,00000000000000220000000000000023,4.9e-44,36,37,5.3e-44,1.93e-322,40,41,42,000000000000002b000000000000002c,6.2e-44,45,46,6.6e-44,2.37e-322,49,50,51,00000000000000340000000000000035,7.4e-44,54,55,7.8e-44,2.8e-322,58,59,60,000000000000003d000000000000003e,8.7e-44,63,64,9.1e-44,3.26e-322,67,68,69,00000000000000460000000000000047,1e-43,72,73,1.04e-43,3.7e-322,76,77,78,000000000000004f0000000000000050,1.12e-43,81,82,1.16e-43,4.15e-322,85,86,87,00000000000000580000000000000059,1.25e-43)
   769  <-- 11
   770  `, "\n"+buf.String())
   771  }
   772  
   773  func TestListener_long_as_is(t *testing.T) {
   774  	params := []wasm.ValueType{
   775  		i32, i64, i32, i64, i32, i64, i32, i64, i32, i64,
   776  		i32, i64, i32, i64, i32, i64, i32, i64, i32, i64,
   777  	}
   778  
   779  	const paramNum = 20
   780  
   781  	var body []byte
   782  	for i := 0; i < paramNum; i++ {
   783  		body = append(body, wasm.OpcodeLocalGet)
   784  		body = append(body, leb128.EncodeUint32(uint32(i))...)
   785  	}
   786  	body = append(body, wasm.OpcodeEnd)
   787  
   788  	bin := binaryencoding.EncodeModule(&wasm.Module{
   789  		TypeSection:     []wasm.FunctionType{{Results: params, Params: params}},
   790  		ExportSection:   []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}},
   791  		FunctionSection: []wasm.Index{0},
   792  		CodeSection:     []wasm.Code{{Body: body}},
   793  	})
   794  
   795  	var buf bytes.Buffer
   796  	config := opt.NewRuntimeConfigOptimizingCompiler()
   797  	ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&buf))
   798  
   799  	r := wazero.NewRuntimeWithConfig(ctx, config)
   800  	defer func() {
   801  		require.NoError(t, r.Close(ctx))
   802  	}()
   803  
   804  	inst, err := r.Instantiate(ctx, bin)
   805  	require.NoError(t, err)
   806  
   807  	f := inst.ExportedFunction("main")
   808  	require.NotNil(t, f)
   809  	param := make([]uint64, paramNum)
   810  	for i := range param {
   811  		param[i] = uint64(i)
   812  	}
   813  	res, err := f.Call(ctx, param...)
   814  	require.NoError(t, err)
   815  	require.Equal(t, param, res)
   816  
   817  	require.Equal(t, `
   818  --> .$0(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19)
   819  <-- (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19)
   820  `, "\n"+buf.String())
   821  }
   822  
   823  func TestListener_long_many_consts(t *testing.T) {
   824  	const paramNum = 61
   825  
   826  	var exp []uint64
   827  	var body []byte
   828  	var resultTypes []wasm.ValueType
   829  	for i := 0; i < paramNum; i++ {
   830  		exp = append(exp, uint64(i))
   831  		resultTypes = append(resultTypes, i32)
   832  		body = append(body, wasm.OpcodeI32Const)
   833  		body = append(body, leb128.EncodeInt32(int32(i))...)
   834  	}
   835  	body = append(body, wasm.OpcodeEnd)
   836  
   837  	bin := binaryencoding.EncodeModule(&wasm.Module{
   838  		TypeSection:     []wasm.FunctionType{{Results: resultTypes}},
   839  		ExportSection:   []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}},
   840  		FunctionSection: []wasm.Index{0},
   841  		CodeSection:     []wasm.Code{{Body: body}},
   842  	})
   843  
   844  	var buf bytes.Buffer
   845  	config := opt.NewRuntimeConfigOptimizingCompiler()
   846  	ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&buf))
   847  
   848  	r := wazero.NewRuntimeWithConfig(ctx, config)
   849  	defer func() {
   850  		require.NoError(t, r.Close(ctx))
   851  	}()
   852  
   853  	inst, err := r.Instantiate(ctx, bin)
   854  	require.NoError(t, err)
   855  
   856  	f := inst.ExportedFunction("main")
   857  	require.NotNil(t, f)
   858  	res, err := f.Call(ctx)
   859  	require.NoError(t, err)
   860  	require.Equal(t, exp, res)
   861  
   862  	require.Equal(t, `
   863  --> .$0()
   864  <-- (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)
   865  `, "\n"+buf.String())
   866  }
   867  
   868  // TestDWARF verifies that the DWARF based stack traces work as expected before/after compilation cache.
   869  func TestDWARF(t *testing.T) {
   870  	config := opt.NewRuntimeConfigOptimizingCompiler()
   871  	ctx := context.Background()
   872  
   873  	bin := dwarftestdata.ZigWasm
   874  
   875  	dir := t.TempDir()
   876  
   877  	var expErr error
   878  	{
   879  		cc, err := wazero.NewCompilationCacheWithDir(dir)
   880  		require.NoError(t, err)
   881  		rc := config.WithCompilationCache(cc)
   882  
   883  		r := wazero.NewRuntimeWithConfig(ctx, rc)
   884  		_, expErr = r.Instantiate(ctx, bin)
   885  		require.Error(t, expErr)
   886  
   887  		err = r.Close(ctx)
   888  		require.NoError(t, err)
   889  	}
   890  
   891  	cc, err := wazero.NewCompilationCacheWithDir(dir)
   892  	require.NoError(t, err)
   893  	rc := config.WithCompilationCache(cc)
   894  	r := wazero.NewRuntimeWithConfig(ctx, rc)
   895  	_, err = r.Instantiate(ctx, bin)
   896  	require.Error(t, err)
   897  	require.Equal(t, expErr.Error(), err.Error())
   898  
   899  	err = r.Close(ctx)
   900  	require.NoError(t, err)
   901  }