github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/engine/compiler/compiler_memory_test.go (about)

     1  package compiler
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"math"
     7  	"testing"
     8  
     9  	"github.com/wasilibs/wazerox/internal/asm"
    10  	"github.com/wasilibs/wazerox/internal/testing/require"
    11  	"github.com/wasilibs/wazerox/internal/wasm"
    12  	"github.com/wasilibs/wazerox/internal/wazeroir"
    13  )
    14  
    15  func TestCompiler_compileMemoryGrow(t *testing.T) {
    16  	env := newCompilerEnvironment()
    17  	compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil)
    18  	err := compiler.compilePreamble()
    19  	require.NoError(t, err)
    20  
    21  	err = compiler.compileMemoryGrow()
    22  	require.NoError(t, err)
    23  
    24  	// Emit arbitrary code after MemoryGrow returned so that we can verify
    25  	// that the code can set the return address properly.
    26  	const expValue uint32 = 100
    27  	err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(expValue)))
    28  	require.NoError(t, err)
    29  	err = compiler.compileReturnFunction()
    30  	require.NoError(t, err)
    31  
    32  	code := asm.CodeSegment{}
    33  	defer func() { require.NoError(t, code.Unmap()) }()
    34  
    35  	// Generate and run the code under test.
    36  	_, err = compiler.compile(code.NextCodeSection())
    37  	require.NoError(t, err)
    38  	env.exec(code.Bytes())
    39  
    40  	// After the initial exec, the code must exit with builtin function call status and funcaddress for memory grow.
    41  	require.Equal(t, nativeCallStatusCodeCallBuiltInFunction, env.compilerStatus())
    42  	require.Equal(t, builtinFunctionIndexMemoryGrow, env.builtinFunctionCallAddress())
    43  
    44  	// Reenter from the return address.
    45  	nativecall(
    46  		env.ce.returnAddress,
    47  		env.callEngine(),
    48  		env.module(),
    49  	)
    50  
    51  	// Check if the code successfully executed the code after builtin function call.
    52  	require.Equal(t, expValue, env.stackTopAsUint32())
    53  	require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus())
    54  }
    55  
    56  func TestCompiler_compileMemorySize(t *testing.T) {
    57  	env := newCompilerEnvironment()
    58  	compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, &wazeroir.CompilationResult{HasMemory: true})
    59  
    60  	err := compiler.compilePreamble()
    61  	require.NoError(t, err)
    62  
    63  	// Emit memory.size instructions.
    64  	err = compiler.compileMemorySize()
    65  	require.NoError(t, err)
    66  	// At this point, the size of memory should be pushed onto the stack.
    67  	requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler)
    68  
    69  	err = compiler.compileReturnFunction()
    70  	require.NoError(t, err)
    71  
    72  	code := asm.CodeSegment{}
    73  	defer func() { require.NoError(t, code.Unmap()) }()
    74  
    75  	// Generate and run the code under test.
    76  	_, err = compiler.compile(code.NextCodeSection())
    77  	require.NoError(t, err)
    78  	env.exec(code.Bytes())
    79  
    80  	require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus())
    81  	require.Equal(t, uint32(defaultMemoryPageNumInTest), env.stackTopAsUint32())
    82  }
    83  
    84  func TestCompiler_compileLoad(t *testing.T) {
    85  	// For testing. Arbitrary number is fine.
    86  	loadTargetValue := uint64(0x12_34_56_78_9a_bc_ef_fe)
    87  	baseOffset := uint32(100)
    88  	arg := wazeroir.MemoryArg{Offset: 361}
    89  	offset := baseOffset + arg.Offset
    90  
    91  	tests := []struct {
    92  		name                string
    93  		isFloatTarget       bool
    94  		operationSetupFn    func(t *testing.T, compiler compilerImpl)
    95  		loadedValueVerifyFn func(t *testing.T, loadedValueAsUint64 uint64)
    96  	}{
    97  		{
    98  			name: "i32.load",
    99  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   100  				err := compiler.compileLoad(operationPtr(wazeroir.NewOperationLoad(wazeroir.UnsignedTypeI32, arg)))
   101  				require.NoError(t, err)
   102  			},
   103  			loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) {
   104  				require.Equal(t, uint32(loadTargetValue), uint32(loadedValueAsUint64))
   105  			},
   106  		},
   107  		{
   108  			name: "i64.load",
   109  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   110  				err := compiler.compileLoad(operationPtr(wazeroir.NewOperationLoad(wazeroir.UnsignedTypeI64, arg)))
   111  				require.NoError(t, err)
   112  			},
   113  			loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) {
   114  				require.Equal(t, loadTargetValue, loadedValueAsUint64)
   115  			},
   116  		},
   117  		{
   118  			name: "f32.load",
   119  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   120  				err := compiler.compileLoad(operationPtr(wazeroir.NewOperationLoad(wazeroir.UnsignedTypeF32, arg)))
   121  				require.NoError(t, err)
   122  			},
   123  			loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) {
   124  				require.Equal(t, uint32(loadTargetValue), uint32(loadedValueAsUint64))
   125  			},
   126  			isFloatTarget: true,
   127  		},
   128  		{
   129  			name: "f64.load",
   130  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   131  				err := compiler.compileLoad(operationPtr(wazeroir.NewOperationLoad(wazeroir.UnsignedTypeF64, arg)))
   132  				require.NoError(t, err)
   133  			},
   134  			loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) {
   135  				require.Equal(t, loadTargetValue, loadedValueAsUint64)
   136  			},
   137  			isFloatTarget: true,
   138  		},
   139  		{
   140  			name: "i32.load8s",
   141  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   142  				err := compiler.compileLoad8(operationPtr(wazeroir.NewOperationLoad8(wazeroir.SignedInt32, arg)))
   143  				require.NoError(t, err)
   144  			},
   145  			loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) {
   146  				require.Equal(t, int32(int8(loadedValueAsUint64)), int32(uint32(loadedValueAsUint64)))
   147  			},
   148  		},
   149  		{
   150  			name: "i32.load8u",
   151  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   152  				err := compiler.compileLoad8(operationPtr(wazeroir.NewOperationLoad8(wazeroir.SignedUint32, arg)))
   153  				require.NoError(t, err)
   154  			},
   155  			loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) {
   156  				require.Equal(t, uint32(byte(loadedValueAsUint64)), uint32(loadedValueAsUint64))
   157  			},
   158  		},
   159  		{
   160  			name: "i64.load8s",
   161  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   162  				err := compiler.compileLoad8(operationPtr(wazeroir.NewOperationLoad8(wazeroir.SignedInt64, arg)))
   163  				require.NoError(t, err)
   164  			},
   165  			loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) {
   166  				require.Equal(t, int64(int8(loadedValueAsUint64)), int64(loadedValueAsUint64))
   167  			},
   168  		},
   169  		{
   170  			name: "i64.load8u",
   171  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   172  				err := compiler.compileLoad8(operationPtr(wazeroir.NewOperationLoad8(wazeroir.SignedUint64, arg)))
   173  				require.NoError(t, err)
   174  			},
   175  			loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) {
   176  				require.Equal(t, uint64(byte(loadedValueAsUint64)), loadedValueAsUint64)
   177  			},
   178  		},
   179  		{
   180  			name: "i32.load16s",
   181  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   182  				err := compiler.compileLoad16(operationPtr(wazeroir.NewOperationLoad16(wazeroir.SignedInt32, arg)))
   183  				require.NoError(t, err)
   184  			},
   185  			loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) {
   186  				require.Equal(t, int32(int16(loadedValueAsUint64)), int32(uint32(loadedValueAsUint64)))
   187  			},
   188  		},
   189  		{
   190  			name: "i32.load16u",
   191  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   192  				err := compiler.compileLoad16(operationPtr(wazeroir.NewOperationLoad16(wazeroir.SignedUint32, arg)))
   193  				require.NoError(t, err)
   194  			},
   195  			loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) {
   196  				require.Equal(t, uint32(loadedValueAsUint64), uint32(loadedValueAsUint64))
   197  			},
   198  		},
   199  		{
   200  			name: "i64.load16s",
   201  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   202  				err := compiler.compileLoad16(operationPtr(wazeroir.NewOperationLoad16(wazeroir.SignedInt64, arg)))
   203  				require.NoError(t, err)
   204  			},
   205  			loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) {
   206  				require.Equal(t, int64(int16(loadedValueAsUint64)), int64(loadedValueAsUint64))
   207  			},
   208  		},
   209  		{
   210  			name: "i64.load16u",
   211  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   212  				err := compiler.compileLoad16(operationPtr(wazeroir.NewOperationLoad16(wazeroir.SignedUint64, arg)))
   213  				require.NoError(t, err)
   214  			},
   215  			loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) {
   216  				require.Equal(t, uint64(uint16(loadedValueAsUint64)), loadedValueAsUint64)
   217  			},
   218  		},
   219  		{
   220  			name: "i64.load32s",
   221  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   222  				err := compiler.compileLoad32(operationPtr(wazeroir.NewOperationLoad32(true, arg)))
   223  				require.NoError(t, err)
   224  			},
   225  			loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) {
   226  				require.Equal(t, int64(int32(loadedValueAsUint64)), int64(loadedValueAsUint64))
   227  			},
   228  		},
   229  		{
   230  			name: "i64.load32u",
   231  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   232  				err := compiler.compileLoad32(operationPtr(wazeroir.NewOperationLoad32(false, arg)))
   233  				require.NoError(t, err)
   234  			},
   235  			loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) {
   236  				require.Equal(t, uint64(uint32(loadedValueAsUint64)), loadedValueAsUint64)
   237  			},
   238  		},
   239  	}
   240  
   241  	for _, tt := range tests {
   242  		tc := tt
   243  		t.Run(tc.name, func(t *testing.T) {
   244  			env := newCompilerEnvironment()
   245  			compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, &wazeroir.CompilationResult{HasMemory: true})
   246  
   247  			err := compiler.compilePreamble()
   248  			require.NoError(t, err)
   249  
   250  			binary.LittleEndian.PutUint64(env.memory()[offset:], loadTargetValue)
   251  
   252  			// Before load operation, we must push the base offset value.
   253  			err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(baseOffset)))
   254  			require.NoError(t, err)
   255  
   256  			tc.operationSetupFn(t, compiler)
   257  
   258  			// At this point, the loaded value must be on top of the stack, and placed on a register.
   259  			requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler)
   260  			require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
   261  			loadedLocation := compiler.runtimeValueLocationStack().peek()
   262  			require.True(t, loadedLocation.onRegister())
   263  			if tc.isFloatTarget {
   264  				require.Equal(t, registerTypeVector, loadedLocation.getRegisterType())
   265  			} else {
   266  				require.Equal(t, registerTypeGeneralPurpose, loadedLocation.getRegisterType())
   267  			}
   268  			err = compiler.compileReturnFunction()
   269  			require.NoError(t, err)
   270  
   271  			code := asm.CodeSegment{}
   272  			defer func() { require.NoError(t, code.Unmap()) }()
   273  
   274  			// Generate and run the code under test.
   275  			_, err = compiler.compile(code.NextCodeSection())
   276  			require.NoError(t, err)
   277  			env.exec(code.Bytes())
   278  
   279  			// Verify the loaded value.
   280  			require.Equal(t, uint64(1), env.stackPointer())
   281  			tc.loadedValueVerifyFn(t, env.stackTopAsUint64())
   282  		})
   283  	}
   284  }
   285  
   286  func TestCompiler_compileStore(t *testing.T) {
   287  	// For testing. Arbitrary number is fine.
   288  	storeTargetValue := uint64(math.MaxUint64)
   289  	baseOffset := uint32(100)
   290  	arg := wazeroir.MemoryArg{Offset: 361}
   291  	offset := arg.Offset + baseOffset
   292  
   293  	tests := []struct {
   294  		name                string
   295  		isFloatTarget       bool
   296  		targetSizeInBytes   uint32
   297  		operationSetupFn    func(t *testing.T, compiler compilerImpl)
   298  		storedValueVerifyFn func(t *testing.T, mem []byte)
   299  	}{
   300  		{
   301  			name:              "i32.store",
   302  			targetSizeInBytes: 32 / 8,
   303  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   304  				err := compiler.compileStore(operationPtr(wazeroir.NewOperationStore(wazeroir.UnsignedTypeI32, arg)))
   305  				require.NoError(t, err)
   306  			},
   307  			storedValueVerifyFn: func(t *testing.T, mem []byte) {
   308  				require.Equal(t, uint32(storeTargetValue), binary.LittleEndian.Uint32(mem[offset:]))
   309  			},
   310  		},
   311  		{
   312  			name:              "f32.store",
   313  			isFloatTarget:     true,
   314  			targetSizeInBytes: 32 / 8,
   315  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   316  				err := compiler.compileStore(operationPtr(wazeroir.NewOperationStore(wazeroir.UnsignedTypeF32, arg)))
   317  				require.NoError(t, err)
   318  			},
   319  			storedValueVerifyFn: func(t *testing.T, mem []byte) {
   320  				require.Equal(t, uint32(storeTargetValue), binary.LittleEndian.Uint32(mem[offset:]))
   321  			},
   322  		},
   323  		{
   324  			name:              "i64.store",
   325  			targetSizeInBytes: 64 / 8,
   326  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   327  				err := compiler.compileStore(operationPtr(wazeroir.NewOperationStore(wazeroir.UnsignedTypeI64, arg)))
   328  				require.NoError(t, err)
   329  			},
   330  			storedValueVerifyFn: func(t *testing.T, mem []byte) {
   331  				require.Equal(t, storeTargetValue, binary.LittleEndian.Uint64(mem[offset:]))
   332  			},
   333  		},
   334  		{
   335  			name:              "f64.store",
   336  			isFloatTarget:     true,
   337  			targetSizeInBytes: 64 / 8,
   338  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   339  				err := compiler.compileStore(operationPtr(wazeroir.NewOperationStore(wazeroir.UnsignedTypeF64, arg)))
   340  				require.NoError(t, err)
   341  			},
   342  			storedValueVerifyFn: func(t *testing.T, mem []byte) {
   343  				require.Equal(t, storeTargetValue, binary.LittleEndian.Uint64(mem[offset:]))
   344  			},
   345  		},
   346  		{
   347  			name:              "store8",
   348  			targetSizeInBytes: 1,
   349  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   350  				err := compiler.compileStore8(operationPtr(wazeroir.NewOperationStore8(arg)))
   351  				require.NoError(t, err)
   352  			},
   353  			storedValueVerifyFn: func(t *testing.T, mem []byte) {
   354  				require.Equal(t, byte(storeTargetValue), mem[offset])
   355  			},
   356  		},
   357  		{
   358  			name:              "store16",
   359  			targetSizeInBytes: 16 / 8,
   360  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   361  				err := compiler.compileStore16(operationPtr(wazeroir.NewOperationStore16(arg)))
   362  				require.NoError(t, err)
   363  			},
   364  			storedValueVerifyFn: func(t *testing.T, mem []byte) {
   365  				require.Equal(t, uint16(storeTargetValue), binary.LittleEndian.Uint16(mem[offset:]))
   366  			},
   367  		},
   368  		{
   369  			name:              "store32",
   370  			targetSizeInBytes: 32 / 8,
   371  			operationSetupFn: func(t *testing.T, compiler compilerImpl) {
   372  				err := compiler.compileStore32(operationPtr(wazeroir.NewOperationStore32(arg)))
   373  				require.NoError(t, err)
   374  			},
   375  			storedValueVerifyFn: func(t *testing.T, mem []byte) {
   376  				require.Equal(t, uint32(storeTargetValue), binary.LittleEndian.Uint32(mem[offset:]))
   377  			},
   378  		},
   379  	}
   380  
   381  	for _, tt := range tests {
   382  		tc := tt
   383  		t.Run(tc.name, func(t *testing.T) {
   384  			env := newCompilerEnvironment()
   385  			compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, &wazeroir.CompilationResult{HasMemory: true})
   386  
   387  			err := compiler.compilePreamble()
   388  			require.NoError(t, err)
   389  
   390  			// Before store operations, we must push the base offset, and the store target values.
   391  			err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(baseOffset)))
   392  			require.NoError(t, err)
   393  			if tc.isFloatTarget {
   394  				err = compiler.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(math.Float64frombits(storeTargetValue))))
   395  			} else {
   396  				err = compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(storeTargetValue)))
   397  			}
   398  			require.NoError(t, err)
   399  
   400  			tc.operationSetupFn(t, compiler)
   401  
   402  			// At this point, no registers must be in use, and no values on the stack since we consumed two values.
   403  			require.Zero(t, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
   404  			requireRuntimeLocationStackPointerEqual(t, uint64(0), compiler)
   405  
   406  			code := asm.CodeSegment{}
   407  			defer func() { require.NoError(t, code.Unmap()) }()
   408  
   409  			// Generate the code under test.
   410  			err = compiler.compileReturnFunction()
   411  			require.NoError(t, err)
   412  			_, err = compiler.compile(code.NextCodeSection())
   413  			require.NoError(t, err)
   414  
   415  			// Set the value on the left and right neighboring memoryregion,
   416  			// so that we can verify the operation doesn't affect there.
   417  			ceil := offset + tc.targetSizeInBytes
   418  			mem := env.memory()
   419  			expectedNeighbor8Bytes := uint64(0x12_34_56_78_9a_bc_ef_fe)
   420  			binary.LittleEndian.PutUint64(mem[offset-8:offset], expectedNeighbor8Bytes)
   421  			binary.LittleEndian.PutUint64(mem[ceil:ceil+8], expectedNeighbor8Bytes)
   422  
   423  			// Run code.
   424  			env.exec(code.Bytes())
   425  
   426  			tc.storedValueVerifyFn(t, mem)
   427  
   428  			// The neighboring bytes must be intact.
   429  			require.Equal(t, expectedNeighbor8Bytes, binary.LittleEndian.Uint64(mem[offset-8:offset]))
   430  			require.Equal(t, expectedNeighbor8Bytes, binary.LittleEndian.Uint64(mem[ceil:ceil+8]))
   431  		})
   432  	}
   433  }
   434  
   435  func TestCompiler_MemoryOutOfBounds(t *testing.T) {
   436  	bases := []uint32{0, 1 << 5, 1 << 9, 1 << 10, 1 << 15, math.MaxUint32 - 1, math.MaxUint32}
   437  	offsets := []uint32{
   438  		0,
   439  		1 << 10, 1 << 31,
   440  		defaultMemoryPageNumInTest*wasm.MemoryPageSize - 1, defaultMemoryPageNumInTest * wasm.MemoryPageSize,
   441  		math.MaxInt32 - 1, math.MaxInt32 - 2, math.MaxInt32 - 3, math.MaxInt32 - 4,
   442  		math.MaxInt32 - 5, math.MaxInt32 - 8, math.MaxInt32 - 9, math.MaxInt32, math.MaxUint32,
   443  	}
   444  	targetSizeInBytes := []int64{1, 2, 4, 8}
   445  	for _, base := range bases {
   446  		base := base
   447  		for _, offset := range offsets {
   448  			offset := offset
   449  			for _, targetSizeInByte := range targetSizeInBytes {
   450  				targetSizeInByte := targetSizeInByte
   451  				t.Run(fmt.Sprintf("base=%d,offset=%d,targetSizeInBytes=%d", base, offset, targetSizeInByte), func(t *testing.T) {
   452  					env := newCompilerEnvironment()
   453  					compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil)
   454  
   455  					err := compiler.compilePreamble()
   456  					require.NoError(t, err)
   457  
   458  					err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(base)))
   459  					require.NoError(t, err)
   460  
   461  					arg := wazeroir.MemoryArg{Offset: offset}
   462  
   463  					switch targetSizeInByte {
   464  					case 1:
   465  						err = compiler.compileLoad8(operationPtr(wazeroir.NewOperationLoad8(wazeroir.SignedInt32, arg)))
   466  					case 2:
   467  						err = compiler.compileLoad16(operationPtr(wazeroir.NewOperationLoad16(wazeroir.SignedInt32, arg)))
   468  					case 4:
   469  						err = compiler.compileLoad32(operationPtr(wazeroir.NewOperationLoad32(false, arg)))
   470  					case 8:
   471  						err = compiler.compileLoad(operationPtr(wazeroir.NewOperationLoad(wazeroir.UnsignedTypeF64, arg)))
   472  					default:
   473  						t.Fail()
   474  					}
   475  
   476  					require.NoError(t, err)
   477  					require.NoError(t, compiler.compileReturnFunction())
   478  
   479  					code := asm.CodeSegment{}
   480  					defer func() { require.NoError(t, code.Unmap()) }()
   481  
   482  					// Generate the code under test and run.
   483  					_, err = compiler.compile(code.NextCodeSection())
   484  					require.NoError(t, err)
   485  					env.exec(code.Bytes())
   486  
   487  					mem := env.memory()
   488  					if ceil := int64(base) + int64(offset) + int64(targetSizeInByte); int64(len(mem)) < ceil {
   489  						// If the targe memory region's ceil exceeds the length of memory, we must exit the function
   490  						// with nativeCallStatusCodeMemoryOutOfBounds status code.
   491  						require.Equal(t, nativeCallStatusCodeMemoryOutOfBounds, env.compilerStatus())
   492  					}
   493  				})
   494  			}
   495  		}
   496  	}
   497  }