github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/engine/interpreter/interpreter_test.go (about)

     1  package interpreter
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math"
     7  	"strconv"
     8  	"testing"
     9  
    10  	"github.com/bananabytelabs/wazero/api"
    11  	"github.com/bananabytelabs/wazero/internal/testing/require"
    12  	"github.com/bananabytelabs/wazero/internal/wasm"
    13  	"github.com/bananabytelabs/wazero/internal/wazeroir"
    14  )
    15  
    16  // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
    17  var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
    18  
    19  func TestInterpreter_peekValues(t *testing.T) {
    20  	ce := &callEngine{}
    21  	require.Nil(t, ce.peekValues(0))
    22  
    23  	ce.stack = []uint64{5, 4, 3, 2, 1}
    24  	require.Nil(t, ce.peekValues(0))
    25  	require.Equal(t, []uint64{2, 1}, ce.peekValues(2))
    26  }
    27  
    28  func TestInterpreter_CallEngine_PushFrame(t *testing.T) {
    29  	f1 := &callFrame{}
    30  	f2 := &callFrame{}
    31  
    32  	ce := callEngine{}
    33  	require.Zero(t, len(ce.frames), "expected no frames")
    34  
    35  	ce.pushFrame(f1)
    36  	require.Equal(t, []*callFrame{f1}, ce.frames)
    37  
    38  	ce.pushFrame(f2)
    39  	require.Equal(t, []*callFrame{f1, f2}, ce.frames)
    40  }
    41  
    42  func TestInterpreter_CallEngine_PushFrame_StackOverflow(t *testing.T) {
    43  	saved := callStackCeiling
    44  	defer func() { callStackCeiling = saved }()
    45  
    46  	callStackCeiling = 3
    47  
    48  	f1 := &callFrame{}
    49  	f2 := &callFrame{}
    50  	f3 := &callFrame{}
    51  	f4 := &callFrame{}
    52  
    53  	vm := callEngine{}
    54  	vm.pushFrame(f1)
    55  	vm.pushFrame(f2)
    56  	vm.pushFrame(f3)
    57  
    58  	captured := require.CapturePanic(func() { vm.pushFrame(f4) })
    59  	require.EqualError(t, captured, "stack overflow")
    60  }
    61  
    62  func TestInterpreter_NonTrappingFloatToIntConversion(t *testing.T) {
    63  	_0x80000000 := uint32(0x80000000)
    64  	_0xffffffff := uint32(0xffffffff)
    65  	_0x8000000000000000 := uint64(0x8000000000000000)
    66  	_0xffffffffffffffff := uint64(0xffffffffffffffff)
    67  
    68  	tests := []struct {
    69  		op            wasm.OpcodeMisc
    70  		inputType     wazeroir.Float
    71  		outputType    wazeroir.SignedInt
    72  		input32bit    []float32
    73  		input64bit    []float64
    74  		expected32bit []int32
    75  		expected64bit []int64
    76  	}{
    77  		{
    78  			// https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L261-L282
    79  			op:         wasm.OpcodeMiscI32TruncSatF32S,
    80  			inputType:  wazeroir.Float32,
    81  			outputType: wazeroir.SignedInt32,
    82  			input32bit: []float32{
    83  				0.0, 0.0, 0x1p-149, -0x1p-149, 1.0, 0x1.19999ap+0, 1.5, -1.0, -0x1.19999ap+0,
    84  				-1.5, -1.9, -2.0, 2147483520.0, -2147483648.0, 2147483648.0, -2147483904.0,
    85  				float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN()), float32(math.NaN()),
    86  				float32(math.NaN()), float32(math.NaN()),
    87  			},
    88  			expected32bit: []int32{
    89  				0, 0, 0, 0, 1, 1, 1, -1, -1, -1, -1, -2, 2147483520, -2147483648, 0x7fffffff,
    90  				int32(_0x80000000), 0x7fffffff, int32(_0x80000000), 0, 0, 0, 0,
    91  			},
    92  		},
    93  		{
    94  			// https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L284-L304
    95  			op:         wasm.OpcodeMiscI32TruncSatF32U,
    96  			inputType:  wazeroir.Float32,
    97  			outputType: wazeroir.SignedUint32,
    98  			input32bit: []float32{
    99  				0.0, 0.0, 0x1p-149, -0x1p-149, 1.0, 0x1.19999ap+0, 1.5, 1.9, 2.0, 2147483648, 4294967040.0,
   100  				-0x1.ccccccp-1, -0x1.fffffep-1, 4294967296.0, -1.0, float32(math.Inf(1)), float32(math.Inf(-1)),
   101  				float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN()),
   102  			},
   103  			expected32bit: []int32{
   104  				0, 0, 0, 0, 1, 1, 1, 1, 2, -2147483648, -256, 0, 0, int32(_0xffffffff), 0x00000000,
   105  				int32(_0xffffffff), 0x00000000, 0, 0, 0, 0,
   106  			},
   107  		},
   108  		{
   109  			// https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L355-L378
   110  			op:         wasm.OpcodeMiscI64TruncSatF32S,
   111  			inputType:  wazeroir.Float32,
   112  			outputType: wazeroir.SignedInt64,
   113  			input32bit: []float32{
   114  				0.0, 0.0, 0x1p-149, -0x1p-149, 1.0, 0x1.19999ap+0, 1.5, -1.0, -0x1.19999ap+0, -1.5, -1.9, -2.0, 4294967296,
   115  				-4294967296, 9223371487098961920.0, -9223372036854775808.0, 9223372036854775808.0, -9223373136366403584.0,
   116  				float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN()), float32(math.NaN()), float32(math.NaN()),
   117  				float32(math.NaN()),
   118  			},
   119  			expected64bit: []int64{
   120  				0, 0, 0, 0, 1, 1, 1, -1, -1, -1, -1, -2, 4294967296, -4294967296, 9223371487098961920, -9223372036854775808,
   121  				0x7fffffffffffffff, int64(_0x8000000000000000), 0x7fffffffffffffff, int64(_0x8000000000000000), 0, 0, 0, 0,
   122  			},
   123  		},
   124  		{
   125  			// https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L380-L398
   126  			op:         wasm.OpcodeMiscI64TruncSatF32U,
   127  			inputType:  wazeroir.Float32,
   128  			outputType: wazeroir.SignedUint64,
   129  			input32bit: []float32{
   130  				0.0, 0.0, 0x1p-149, -0x1p-149, 1.0, 0x1.19999ap+0, 1.5, 4294967296,
   131  				18446742974197923840.0, -0x1.ccccccp-1, -0x1.fffffep-1, 18446744073709551616.0, -1.0,
   132  				float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN()), float32(math.NaN()),
   133  				float32(math.NaN()), float32(math.NaN()),
   134  			},
   135  			expected64bit: []int64{
   136  				0, 0, 0, 0, 1, 1, 1,
   137  				4294967296, -1099511627776, 0, 0, int64(_0xffffffffffffffff), 0x0000000000000000,
   138  				int64(_0xffffffffffffffff), 0x0000000000000000, 0, 0, 0, 0,
   139  			},
   140  		},
   141  		{
   142  			// https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L306-L327
   143  			op:         wasm.OpcodeMiscI32TruncSatF64S,
   144  			inputType:  wazeroir.Float64,
   145  			outputType: wazeroir.SignedInt32,
   146  			input64bit: []float64{
   147  				0.0, 0.0, 0x0.0000000000001p-1022, -0x0.0000000000001p-1022, 1.0, 0x1.199999999999ap+0, 1.5, -1.0,
   148  				-0x1.199999999999ap+0, -1.5, -1.9, -2.0, 2147483647.0, -2147483648.0, 2147483648.0,
   149  				-2147483649.0, math.Inf(1), math.Inf(-1), math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   150  			},
   151  			expected32bit: []int32{
   152  				0, 0, 0, 0, 1, 1, 1, -1, -1, -1, -1, -2,
   153  				2147483647, -2147483648, 0x7fffffff, int32(_0x80000000), 0x7fffffff, int32(_0x80000000), 0,
   154  				0, 0, 0,
   155  			},
   156  		},
   157  		{
   158  			// https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L329-L353
   159  			op:         wasm.OpcodeMiscI32TruncSatF64U,
   160  			inputType:  wazeroir.Float64,
   161  			outputType: wazeroir.SignedUint32,
   162  			input64bit: []float64{
   163  				0.0, 0.0, 0x0.0000000000001p-1022, -0x0.0000000000001p-1022, 1.0, 0x1.199999999999ap+0, 1.5, 1.9, 2.0,
   164  				2147483648, 4294967295.0, -0x1.ccccccccccccdp-1, -0x1.fffffffffffffp-1, 1e8, 4294967296.0, -1.0, 1e16, 1e30,
   165  				9223372036854775808, math.Inf(1), math.Inf(-1), math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   166  			},
   167  			expected32bit: []int32{
   168  				0, 0, 0, 0, 1, 1, 1, 1, 2, -2147483648, -1,
   169  				0, 0, 100000000, int32(_0xffffffff), 0x00000000, int32(_0xffffffff), int32(_0xffffffff), int32(_0xffffffff),
   170  				int32(_0xffffffff), 0x00000000, 0, 0, 0, 0,
   171  			},
   172  		},
   173  		{
   174  			// https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L400-L423
   175  			op:         wasm.OpcodeMiscI64TruncSatF64S,
   176  			inputType:  wazeroir.Float64,
   177  			outputType: wazeroir.SignedInt64,
   178  			input64bit: []float64{
   179  				0.0, 0.0, 0x0.0000000000001p-1022, -0x0.0000000000001p-1022, 1.0, 0x1.199999999999ap+0, 1.5, -1.0,
   180  				-0x1.199999999999ap+0, -1.5, -1.9, -2.0, 4294967296, -4294967296, 9223372036854774784.0, -9223372036854775808.0,
   181  				9223372036854775808.0, -9223372036854777856.0, math.Inf(1), math.Inf(-1), math.NaN(), math.NaN(), math.NaN(),
   182  				math.NaN(),
   183  			},
   184  			expected64bit: []int64{
   185  				0, 0, 0, 0, 1, 1, 1, -1, -1, -1, -1, -2,
   186  				4294967296, -4294967296, 9223372036854774784, -9223372036854775808, 0x7fffffffffffffff,
   187  				int64(_0x8000000000000000), 0x7fffffffffffffff, int64(_0x8000000000000000), 0, 0, 0, 0,
   188  			},
   189  		},
   190  		{
   191  			// https://github.com/WebAssembly/spec/blob/c8fd933fa51eb0b511bce027b573aef7ee373726/test/core/conversions.wast#L425-L447
   192  			op:         wasm.OpcodeMiscI64TruncSatF64U,
   193  			inputType:  wazeroir.Float64,
   194  			outputType: wazeroir.SignedUint64,
   195  			input64bit: []float64{
   196  				0.0, 0.0, 0x0.0000000000001p-1022, -0x0.0000000000001p-1022, 1.0, 0x1.199999999999ap+0, 1.5, 4294967295, 4294967296,
   197  				18446744073709549568.0, -0x1.ccccccccccccdp-1, -0x1.fffffffffffffp-1, 1e8, 1e16, 9223372036854775808,
   198  				18446744073709551616.0, -1.0, math.Inf(1), math.Inf(-1), math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   199  			},
   200  			expected64bit: []int64{
   201  				0, 0, 0, 0, 1, 1, 1, 0xffffffff, 0x100000000, -2048, 0, 0, 100000000, 10000000000000000,
   202  				-9223372036854775808, int64(_0xffffffffffffffff), 0x0000000000000000, int64(_0xffffffffffffffff),
   203  				0x0000000000000000, 0, 0, 0, 0,
   204  			},
   205  		},
   206  	}
   207  
   208  	for _, tt := range tests {
   209  		tc := tt
   210  		t.Run(wasm.MiscInstructionName(tc.op), func(t *testing.T) {
   211  			in32bit := len(tc.input32bit) > 0
   212  			casenum := len(tc.input32bit)
   213  			if !in32bit {
   214  				casenum = len(tc.input64bit)
   215  			}
   216  			for i := 0; i < casenum; i++ {
   217  				i := i
   218  				t.Run(strconv.Itoa(i), func(t *testing.T) {
   219  					var body []wazeroir.UnionOperation
   220  					if in32bit {
   221  						body = append(body, wazeroir.UnionOperation{
   222  							Kind: wazeroir.OperationKindConstF32,
   223  							U1:   uint64(math.Float32bits(tc.input32bit[i])),
   224  						})
   225  					} else {
   226  						body = append(body, wazeroir.UnionOperation{
   227  							Kind: wazeroir.OperationKindConstF64,
   228  							U1:   uint64(math.Float64bits(tc.input64bit[i])),
   229  						})
   230  					}
   231  
   232  					body = append(body, wazeroir.UnionOperation{
   233  						Kind: wazeroir.OperationKindITruncFromF,
   234  						B1:   byte(tc.inputType),
   235  						B2:   byte(tc.outputType),
   236  						B3:   true, // NonTrapping = true.
   237  					})
   238  
   239  					// Return from function.
   240  					body = append(body,
   241  						wazeroir.UnionOperation{Kind: wazeroir.OperationKindBr, U1: uint64(math.MaxUint64)},
   242  					)
   243  
   244  					ce := &callEngine{}
   245  					f := &function{
   246  						moduleInstance: &wasm.ModuleInstance{Engine: &moduleEngine{}},
   247  						parent:         &compiledFunction{body: body},
   248  					}
   249  					ce.callNativeFunc(testCtx, &wasm.ModuleInstance{}, f)
   250  
   251  					if len(tc.expected32bit) > 0 {
   252  						require.Equal(t, tc.expected32bit[i], int32(uint32(ce.popValue())))
   253  					} else {
   254  						require.Equal(t, tc.expected64bit[i], int64((ce.popValue())))
   255  					}
   256  				})
   257  			}
   258  		})
   259  
   260  	}
   261  }
   262  
   263  func TestInterpreter_CallEngine_callNativeFunc_signExtend(t *testing.T) {
   264  	translateToIROperationKind := func(op wasm.Opcode) (kind wazeroir.OperationKind) {
   265  		switch op {
   266  		case wasm.OpcodeI32Extend8S:
   267  			kind = wazeroir.OperationKindSignExtend32From8
   268  		case wasm.OpcodeI32Extend16S:
   269  			kind = wazeroir.OperationKindSignExtend32From16
   270  		case wasm.OpcodeI64Extend8S:
   271  			kind = wazeroir.OperationKindSignExtend64From8
   272  		case wasm.OpcodeI64Extend16S:
   273  			kind = wazeroir.OperationKindSignExtend64From16
   274  		case wasm.OpcodeI64Extend32S:
   275  			kind = wazeroir.OperationKindSignExtend64From32
   276  		}
   277  		return
   278  	}
   279  	t.Run("32bit", func(t *testing.T) {
   280  		tests := []struct {
   281  			in       int32
   282  			expected int32
   283  			opcode   wasm.Opcode
   284  		}{
   285  			// https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i32.wast#L270-L276
   286  			{in: 0, expected: 0, opcode: wasm.OpcodeI32Extend8S},
   287  			{in: 0x7f, expected: 127, opcode: wasm.OpcodeI32Extend8S},
   288  			{in: 0x80, expected: -128, opcode: wasm.OpcodeI32Extend8S},
   289  			{in: 0xff, expected: -1, opcode: wasm.OpcodeI32Extend8S},
   290  			{in: 0x012345_00, expected: 0, opcode: wasm.OpcodeI32Extend8S},
   291  			{in: -19088768 /* = 0xfedcba_80 bit pattern */, expected: -0x80, opcode: wasm.OpcodeI32Extend8S},
   292  			{in: -1, expected: -1, opcode: wasm.OpcodeI32Extend8S},
   293  
   294  			// https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i32.wast#L278-L284
   295  			{in: 0, expected: 0, opcode: wasm.OpcodeI32Extend16S},
   296  			{in: 0x7fff, expected: 32767, opcode: wasm.OpcodeI32Extend16S},
   297  			{in: 0x8000, expected: -32768, opcode: wasm.OpcodeI32Extend16S},
   298  			{in: 0xffff, expected: -1, opcode: wasm.OpcodeI32Extend16S},
   299  			{in: 0x0123_0000, expected: 0, opcode: wasm.OpcodeI32Extend16S},
   300  			{in: -19103744 /* = 0xfedc_8000 bit pattern */, expected: -0x8000, opcode: wasm.OpcodeI32Extend16S},
   301  			{in: -1, expected: -1, opcode: wasm.OpcodeI32Extend16S},
   302  		}
   303  
   304  		for _, tt := range tests {
   305  			tc := tt
   306  			t.Run(fmt.Sprintf("%s(i32.const(0x%x))", wasm.InstructionName(tc.opcode), tc.in), func(t *testing.T) {
   307  				ce := &callEngine{}
   308  				f := &function{
   309  					moduleInstance: &wasm.ModuleInstance{Engine: &moduleEngine{}},
   310  					parent: &compiledFunction{body: []wazeroir.UnionOperation{
   311  						{Kind: wazeroir.OperationKindConstI32, U1: uint64(uint32(tc.in))},
   312  						{Kind: translateToIROperationKind(tc.opcode)},
   313  						{Kind: wazeroir.OperationKindBr, U1: uint64(math.MaxUint64)},
   314  					}},
   315  				}
   316  				ce.callNativeFunc(testCtx, &wasm.ModuleInstance{}, f)
   317  				require.Equal(t, tc.expected, int32(uint32(ce.popValue())))
   318  			})
   319  		}
   320  	})
   321  	t.Run("64bit", func(t *testing.T) {
   322  		tests := []struct {
   323  			in       int64
   324  			expected int64
   325  			opcode   wasm.Opcode
   326  		}{
   327  			// https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i64.wast#L271-L277
   328  			{in: 0, expected: 0, opcode: wasm.OpcodeI64Extend8S},
   329  			{in: 0x7f, expected: 127, opcode: wasm.OpcodeI64Extend8S},
   330  			{in: 0x80, expected: -128, opcode: wasm.OpcodeI64Extend8S},
   331  			{in: 0xff, expected: -1, opcode: wasm.OpcodeI64Extend8S},
   332  			{in: 0x01234567_89abcd_00, expected: 0, opcode: wasm.OpcodeI64Extend8S},
   333  			{in: 81985529216486784 /* = 0xfedcba98_765432_80 bit pattern */, expected: -0x80, opcode: wasm.OpcodeI64Extend8S},
   334  			{in: -1, expected: -1, opcode: wasm.OpcodeI64Extend8S},
   335  
   336  			// https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i64.wast#L279-L285
   337  			{in: 0, expected: 0, opcode: wasm.OpcodeI64Extend16S},
   338  			{in: 0x7fff, expected: 32767, opcode: wasm.OpcodeI64Extend16S},
   339  			{in: 0x8000, expected: -32768, opcode: wasm.OpcodeI64Extend16S},
   340  			{in: 0xffff, expected: -1, opcode: wasm.OpcodeI64Extend16S},
   341  			{in: 0x12345678_9abc_0000, expected: 0, opcode: wasm.OpcodeI64Extend16S},
   342  			{in: 81985529216466944 /* = 0xfedcba98_7654_8000 bit pattern */, expected: -0x8000, opcode: wasm.OpcodeI64Extend16S},
   343  			{in: -1, expected: -1, opcode: wasm.OpcodeI64Extend16S},
   344  
   345  			// https://github.com/WebAssembly/spec/blob/ee4a6c40afa22e3e4c58610ce75186aafc22344e/test/core/i64.wast#L287-L296
   346  			{in: 0, expected: 0, opcode: wasm.OpcodeI64Extend32S},
   347  			{in: 0x7fff, expected: 32767, opcode: wasm.OpcodeI64Extend32S},
   348  			{in: 0x8000, expected: 32768, opcode: wasm.OpcodeI64Extend32S},
   349  			{in: 0xffff, expected: 65535, opcode: wasm.OpcodeI64Extend32S},
   350  			{in: 0x7fffffff, expected: 0x7fffffff, opcode: wasm.OpcodeI64Extend32S},
   351  			{in: 0x80000000, expected: -0x80000000, opcode: wasm.OpcodeI64Extend32S},
   352  			{in: 0xffffffff, expected: -1, opcode: wasm.OpcodeI64Extend32S},
   353  			{in: 0x01234567_00000000, expected: 0, opcode: wasm.OpcodeI64Extend32S},
   354  			{in: -81985529054232576 /* = 0xfedcba98_80000000 bit pattern */, expected: -0x80000000, opcode: wasm.OpcodeI64Extend32S},
   355  			{in: -1, expected: -1, opcode: wasm.OpcodeI64Extend32S},
   356  		}
   357  
   358  		for _, tt := range tests {
   359  			tc := tt
   360  			t.Run(fmt.Sprintf("%s(i64.const(0x%x))", wasm.InstructionName(tc.opcode), tc.in), func(t *testing.T) {
   361  				ce := &callEngine{}
   362  				f := &function{
   363  					moduleInstance: &wasm.ModuleInstance{Engine: &moduleEngine{}},
   364  					parent: &compiledFunction{body: []wazeroir.UnionOperation{
   365  						{Kind: wazeroir.OperationKindConstI64, U1: uint64(tc.in)},
   366  						{Kind: translateToIROperationKind(tc.opcode)},
   367  						{Kind: wazeroir.OperationKindBr, U1: uint64(math.MaxUint64)},
   368  					}},
   369  				}
   370  				ce.callNativeFunc(testCtx, &wasm.ModuleInstance{}, f)
   371  				require.Equal(t, tc.expected, int64(ce.popValue()))
   372  			})
   373  		}
   374  	})
   375  }
   376  
   377  func TestInterpreter_Compile(t *testing.T) {
   378  	t.Run("uncompiled", func(t *testing.T) {
   379  		e := NewEngine(testCtx, api.CoreFeaturesV1, nil).(*engine)
   380  		_, err := e.NewModuleEngine(
   381  			&wasm.Module{},
   382  			nil, // functions
   383  		)
   384  		require.EqualError(t, err, "source module must be compiled before instantiation")
   385  	})
   386  	t.Run("fail", func(t *testing.T) {
   387  		e := NewEngine(testCtx, api.CoreFeaturesV1, nil).(*engine)
   388  
   389  		errModule := &wasm.Module{
   390  			TypeSection:     []wasm.FunctionType{{}},
   391  			FunctionSection: []wasm.Index{0, 0, 0},
   392  			CodeSection: []wasm.Code{
   393  				{Body: []byte{wasm.OpcodeEnd}},
   394  				{Body: []byte{wasm.OpcodeEnd}},
   395  				{Body: []byte{wasm.OpcodeCall}}, // Call instruction without immediate for call target index is invalid and should fail to compile.
   396  			},
   397  			ID: wasm.ModuleID{},
   398  		}
   399  
   400  		err := e.CompileModule(testCtx, errModule, nil, false)
   401  		require.EqualError(t, err, "handling instruction: apply stack failed for call: reading immediates: EOF")
   402  
   403  		// On the compilation failure, all the compiled functions including succeeded ones must be released.
   404  		_, ok := e.compiledFunctions[errModule.ID]
   405  		require.False(t, ok)
   406  	})
   407  	t.Run("ok", func(t *testing.T) {
   408  		e := NewEngine(testCtx, api.CoreFeaturesV1, nil).(*engine)
   409  
   410  		okModule := &wasm.Module{
   411  			TypeSection:     []wasm.FunctionType{{}},
   412  			FunctionSection: []wasm.Index{0, 0, 0, 0},
   413  			CodeSection: []wasm.Code{
   414  				{Body: []byte{wasm.OpcodeEnd}},
   415  				{Body: []byte{wasm.OpcodeEnd}},
   416  				{Body: []byte{wasm.OpcodeEnd}},
   417  				{Body: []byte{wasm.OpcodeEnd}},
   418  			},
   419  			ID: wasm.ModuleID{},
   420  		}
   421  		err := e.CompileModule(testCtx, okModule, nil, false)
   422  		require.NoError(t, err)
   423  
   424  		compiled, ok := e.compiledFunctions[okModule.ID]
   425  		require.True(t, ok)
   426  		require.Equal(t, len(okModule.FunctionSection), len(compiled))
   427  
   428  		_, ok = e.compiledFunctions[okModule.ID]
   429  		require.True(t, ok)
   430  	})
   431  }
   432  
   433  func TestEngine_CachedCompiledFunctionPerModule(t *testing.T) {
   434  	e := NewEngine(testCtx, api.CoreFeaturesV1, nil).(*engine)
   435  	exp := []compiledFunction{
   436  		{body: []wazeroir.UnionOperation{}},
   437  		{body: []wazeroir.UnionOperation{}},
   438  	}
   439  	m := &wasm.Module{}
   440  
   441  	e.addCompiledFunctions(m, exp)
   442  
   443  	actual, ok := e.getCompiledFunctions(m)
   444  	require.True(t, ok)
   445  	require.Equal(t, len(exp), len(actual))
   446  	for i := range actual {
   447  		require.Equal(t, exp[i], actual[i])
   448  	}
   449  
   450  	e.deleteCompiledFunctions(m)
   451  	_, ok = e.getCompiledFunctions(m)
   452  	require.False(t, ok)
   453  }