wa-lang.org/wazero@v1.0.2/internal/gojs/spfunc/spfunc_test.go (about)

     1  package spfunc
     2  
     3  import (
     4  	"context"
     5  	_ "embed"
     6  	"testing"
     7  
     8  	"wa-lang.org/wazero"
     9  	"wa-lang.org/wazero/api"
    10  	"wa-lang.org/wazero/internal/testing/require"
    11  	"wa-lang.org/wazero/internal/wasm"
    12  	"wa-lang.org/wazero/internal/wasm/binary"
    13  )
    14  
    15  const i32, i64 = api.ValueTypeI32, api.ValueTypeI64
    16  
    17  // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
    18  var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
    19  
    20  func Test_callFromSP(t *testing.T) {
    21  	tests := []struct {
    22  		name                string
    23  		expectSP            bool
    24  		inputMem, outputMem []byte
    25  		proxied             *wasm.HostFunc
    26  		expected            *wasm.ProxyFunc
    27  		expectedErr         string
    28  	}{
    29  		{
    30  			name: "i32_v",
    31  			proxied: &wasm.HostFunc{
    32  				ExportNames: []string{"ex"},
    33  				Name:        "n",
    34  				ParamTypes:  []wasm.ValueType{wasm.ValueTypeI32},
    35  				ParamNames:  []string{"x"},
    36  				Code:        &wasm.Code{IsHostFunction: true, Body: []byte{wasm.OpcodeEnd}},
    37  			},
    38  			expected: &wasm.ProxyFunc{
    39  				Proxy: &wasm.HostFunc{
    40  					ExportNames: []string{"ex"},
    41  					Name:        "n.proxy",
    42  					ParamTypes:  []wasm.ValueType{wasm.ValueTypeI32},
    43  					ParamNames:  []string{"sp"},
    44  					Code: &wasm.Code{
    45  						IsHostFunction: true,
    46  						LocalTypes:     nil, // because there are no results
    47  						Body: []byte{
    48  							wasm.OpcodeLocalGet, 0, wasm.OpcodeI32Const, 8, wasm.OpcodeI32Add,
    49  							wasm.OpcodeI32Load, 0x2, 0x0,
    50  							wasm.OpcodeCall, 0,
    51  							wasm.OpcodeEnd,
    52  						},
    53  					},
    54  				},
    55  				Proxied: &wasm.HostFunc{
    56  					Name:       "n",
    57  					ParamTypes: []wasm.ValueType{wasm.ValueTypeI32},
    58  					ParamNames: []string{"x"},
    59  					Code:       &wasm.Code{IsHostFunction: true, Body: []byte{wasm.OpcodeEnd}},
    60  				},
    61  				CallBodyPos: 9,
    62  			},
    63  		},
    64  		{
    65  			name: "i32_i32",
    66  			proxied: &wasm.HostFunc{
    67  				ExportNames: []string{"ex"},
    68  				Name:        "n",
    69  				ParamTypes:  []wasm.ValueType{wasm.ValueTypeI32},
    70  				ParamNames:  []string{"x"},
    71  				ResultTypes: []wasm.ValueType{wasm.ValueTypeI32},
    72  				Code:        &wasm.Code{IsHostFunction: true, Body: []byte{wasm.OpcodeI32Const, 1, wasm.OpcodeEnd}},
    73  			},
    74  			expected: &wasm.ProxyFunc{
    75  				Proxy: &wasm.HostFunc{
    76  					ExportNames: []string{"ex"},
    77  					Name:        "n.proxy",
    78  					ParamTypes:  []wasm.ValueType{wasm.ValueTypeI32},
    79  					ParamNames:  []string{"sp"},
    80  					Code: &wasm.Code{
    81  						IsHostFunction: true,
    82  						LocalTypes:     []api.ValueType{api.ValueTypeI32, api.ValueTypeI64},
    83  						Body: []byte{
    84  							wasm.OpcodeLocalGet, 0, wasm.OpcodeI32Const, 8, wasm.OpcodeI32Add,
    85  							wasm.OpcodeI32Load, 0x2, 0x0,
    86  							wasm.OpcodeCall, 0,
    87  							wasm.OpcodeLocalSet, 1,
    88  							wasm.OpcodeLocalGet, 0, wasm.OpcodeI32Const, 16, wasm.OpcodeI32Add,
    89  							wasm.OpcodeLocalGet, 1,
    90  							wasm.OpcodeI32Store, 0x2, 0x0,
    91  							wasm.OpcodeEnd,
    92  						},
    93  					},
    94  				},
    95  				Proxied: &wasm.HostFunc{
    96  					Name:        "n",
    97  					ParamTypes:  []wasm.ValueType{wasm.ValueTypeI32},
    98  					ParamNames:  []string{"x"},
    99  					ResultTypes: []wasm.ValueType{wasm.ValueTypeI32},
   100  					Code:        &wasm.Code{IsHostFunction: true, Body: []byte{wasm.OpcodeI32Const, 1, wasm.OpcodeEnd}},
   101  				},
   102  				CallBodyPos: 9,
   103  			},
   104  		},
   105  		{
   106  			name: "i32i32_i64",
   107  			proxied: &wasm.HostFunc{
   108  				ExportNames: []string{"ex"},
   109  				Name:        "n",
   110  				ParamTypes:  []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32},
   111  				ParamNames:  []string{"x", "y"},
   112  				ResultTypes: []wasm.ValueType{wasm.ValueTypeI64},
   113  				Code:        &wasm.Code{IsHostFunction: true, Body: []byte{wasm.OpcodeI64Const, 1, wasm.OpcodeEnd}},
   114  			},
   115  			expected: &wasm.ProxyFunc{
   116  				Proxy: &wasm.HostFunc{
   117  					ExportNames: []string{"ex"},
   118  					Name:        "n.proxy",
   119  					ParamTypes:  []wasm.ValueType{wasm.ValueTypeI32},
   120  					ParamNames:  []string{"sp"},
   121  					Code: &wasm.Code{
   122  						IsHostFunction: true,
   123  						LocalTypes:     []api.ValueType{api.ValueTypeI32, api.ValueTypeI64},
   124  						Body: []byte{
   125  							wasm.OpcodeLocalGet, 0, wasm.OpcodeI32Const, 8, wasm.OpcodeI32Add,
   126  							wasm.OpcodeI32Load, 0x2, 0x0,
   127  							wasm.OpcodeLocalGet, 0, wasm.OpcodeI32Const, 16, wasm.OpcodeI32Add,
   128  							wasm.OpcodeI32Load, 0x2, 0x0,
   129  							wasm.OpcodeCall, 0,
   130  							wasm.OpcodeLocalSet, 2,
   131  							wasm.OpcodeLocalGet, 0, wasm.OpcodeI32Const, 24, wasm.OpcodeI32Add,
   132  							wasm.OpcodeLocalGet, 2,
   133  							wasm.OpcodeI64Store, 0x3, 0x0,
   134  							wasm.OpcodeEnd,
   135  						},
   136  					},
   137  				},
   138  				Proxied: &wasm.HostFunc{
   139  					Name:        "n",
   140  					ParamTypes:  []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32},
   141  					ParamNames:  []string{"x", "y"},
   142  					ResultTypes: []wasm.ValueType{wasm.ValueTypeI64},
   143  					Code:        &wasm.Code{IsHostFunction: true, Body: []byte{wasm.OpcodeI64Const, 1, wasm.OpcodeEnd}},
   144  				},
   145  				CallBodyPos: 17,
   146  			},
   147  		},
   148  	}
   149  
   150  	for _, tt := range tests {
   151  		tc := tt
   152  
   153  		t.Run(tc.name, func(t *testing.T) {
   154  			proxy, err := callFromSP(tc.expectSP, tc.proxied)
   155  			if tc.expectedErr != "" {
   156  				require.EqualError(t, err, tc.expectedErr)
   157  			} else {
   158  				require.Equal(t, tc.expected, proxy)
   159  			}
   160  		})
   161  	}
   162  }
   163  
   164  var spMem = []byte{
   165  	0, 0, 0, 0, 0, 0, 0, 0,
   166  	1, 0, 0, 0, 0, 0, 0, 0,
   167  	2, 0, 0, 0, 0, 0, 0, 0,
   168  	3, 0, 0, 0, 0, 0, 0, 0,
   169  	4, 0, 0, 0, 0, 0, 0, 0,
   170  	5, 0, 0, 0, 0, 0, 0, 0,
   171  	6, 0, 0, 0, 0, 0, 0, 0,
   172  	7, 0, 0, 0, 0, 0, 0, 0,
   173  	8, 0, 0, 0, 0, 0, 0, 0,
   174  	9, 0, 0, 0, 0, 0, 0, 0,
   175  	10, 0, 0, 0, 0, 0, 0, 0,
   176  }
   177  
   178  func i64i32i32i32i32_i64i32_withSP(_ context.Context, stack []uint64) {
   179  	vRef := stack[0]
   180  	mAddr := uint32(stack[1])
   181  	mLen := uint32(stack[2])
   182  	argsArray := uint32(stack[3])
   183  	argsLen := uint32(stack[4])
   184  
   185  	if vRef != 1 {
   186  		panic("vRef")
   187  	}
   188  	if mAddr != 2 {
   189  		panic("mAddr")
   190  	}
   191  	if mLen != 3 {
   192  		panic("mLen")
   193  	}
   194  	if argsArray != 4 {
   195  		panic("argsArray")
   196  	}
   197  	if argsLen != 5 {
   198  		panic("argsLen")
   199  	}
   200  
   201  	// set results
   202  	stack[0] = 10
   203  	stack[1] = 20
   204  	stack[2] = 8
   205  }
   206  
   207  func TestMustCallFromSP(t *testing.T) {
   208  	r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfigInterpreter())
   209  	defer r.Close(testCtx)
   210  
   211  	funcName := "i64i32i32i32i32_i64i32_withSP"
   212  	builder := r.NewHostModuleBuilder("go")
   213  	builder.(wasm.ProxyFuncExporter).ExportProxyFunc(MustCallFromSP(true, &wasm.HostFunc{
   214  		ExportNames: []string{funcName},
   215  		Name:        funcName,
   216  		ParamTypes:  []api.ValueType{i64, i32, i32, i32, i32},
   217  		ParamNames:  []string{"v", "mAddr", "mLen", "argsArray", "argsLen"},
   218  		ResultTypes: []api.ValueType{i64, i32, i32},
   219  		Code: &wasm.Code{
   220  			IsHostFunction: true,
   221  			GoFunc:         api.GoFunc(i64i32i32i32i32_i64i32_withSP),
   222  		},
   223  	}))
   224  	im, err := builder.Instantiate(testCtx, r)
   225  	require.NoError(t, err)
   226  
   227  	callDef := im.ExportedFunction(funcName).Definition()
   228  
   229  	bin := binary.EncodeModule(&wasm.Module{
   230  		TypeSection: []*wasm.FunctionType{{
   231  			Params:  callDef.ParamTypes(),
   232  			Results: callDef.ResultTypes(),
   233  		}},
   234  		ImportSection:   []*wasm.Import{{Module: "go", Name: funcName}},
   235  		MemorySection:   &wasm.Memory{Min: 1, Cap: 1, Max: 1},
   236  		FunctionSection: []wasm.Index{0},
   237  		CodeSection: []*wasm.Code{
   238  			{Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeCall, 0, wasm.OpcodeEnd}},
   239  		},
   240  		ExportSection: []*wasm.Export{{Name: funcName, Index: 1}},
   241  	})
   242  	require.NoError(t, err)
   243  
   244  	mod, err := r.InstantiateModuleFromBinary(testCtx, bin)
   245  	require.NoError(t, err)
   246  
   247  	memView, ok := mod.Memory().Read(testCtx, 0, uint32(len(spMem)))
   248  	require.True(t, ok)
   249  	copy(memView, spMem)
   250  
   251  	_, err = mod.ExportedFunction(funcName).Call(testCtx, 0) // SP
   252  	require.NoError(t, err)
   253  	require.Equal(t, []byte{
   254  		7, 0, 0, 0, 0, 0, 0, 0, // 7 left alone as SP was refreshed to 8
   255  		10, 0, 0, 0, 0, 0, 0, 0,
   256  		20, 0, 0, 0, 0, 0, 0, 0,
   257  		10, 0, 0, 0, 0, 0, 0, 0,
   258  	}, memView[56:])
   259  }