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 }