github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/testing/proxy/proxy.go (about) 1 package proxy 2 3 import ( 4 "github.com/tetratelabs/wazero" 5 "github.com/tetratelabs/wazero/api" 6 "github.com/tetratelabs/wazero/experimental" 7 "github.com/tetratelabs/wazero/experimental/logging" 8 "github.com/tetratelabs/wazero/internal/leb128" 9 "github.com/tetratelabs/wazero/internal/testing/binaryencoding" 10 "github.com/tetratelabs/wazero/internal/wasm" 11 ) 12 13 const proxyModuleName = "internal/testing/proxy/proxy.go" 14 15 // NewLoggingListenerFactory is like logging.NewHostLoggingListenerFactory, 16 // except it skips logging proxying functions from NewModuleBinary. 17 func NewLoggingListenerFactory(writer logging.Writer, scopes logging.LogScopes) experimental.FunctionListenerFactory { 18 return &loggingListenerFactory{logging.NewHostLoggingListenerFactory(writer, scopes)} 19 } 20 21 type loggingListenerFactory struct { 22 delegate experimental.FunctionListenerFactory 23 } 24 25 // NewFunctionListener implements the same method as documented on 26 // experimental.FunctionListener. 27 func (f *loggingListenerFactory) NewFunctionListener(fnd api.FunctionDefinition) experimental.FunctionListener { 28 if fnd.ModuleName() == proxyModuleName { 29 return nil // don't log proxy stuff 30 } 31 return f.delegate.NewFunctionListener(fnd) 32 } 33 34 // NewModuleBinary creates the proxy module to proxy a function call against 35 // all the exported functions in `proxyTarget`, and returns its encoded binary. 36 // The resulting module exports the proxy functions whose names are exactly the same 37 // as the proxy destination. 38 // 39 // This is used to test host call implementations. If logging, use 40 // NewLoggingListenerFactory to avoid messages from the proxying module. 41 func NewModuleBinary(moduleName string, proxyTarget wazero.CompiledModule) []byte { 42 funcDefs := proxyTarget.ExportedFunctions() 43 funcNum := uint32(len(funcDefs)) 44 proxyModule := &wasm.Module{ 45 MemorySection: &wasm.Memory{Min: 1}, 46 ExportSection: []wasm.Export{{Name: "memory", Type: api.ExternTypeMemory}}, 47 NameSection: &wasm.NameSection{ModuleName: proxyModuleName}, 48 } 49 var cnt wasm.Index 50 for _, def := range funcDefs { 51 proxyModule.TypeSection = append(proxyModule.TypeSection, wasm.FunctionType{ 52 Params: def.ParamTypes(), Results: def.ResultTypes(), 53 }) 54 55 // Imports the function. 56 name := def.ExportNames()[0] 57 proxyModule.ImportSection = append(proxyModule.ImportSection, wasm.Import{ 58 Module: moduleName, 59 Name: name, 60 DescFunc: cnt, 61 }) 62 63 // Ensures that type of the proxy function matches the imported function. 64 proxyModule.FunctionSection = append(proxyModule.FunctionSection, cnt) 65 66 // Build the function body of the proxy function. 67 var body []byte 68 for i := range def.ParamTypes() { 69 body = append(body, wasm.OpcodeLocalGet) 70 body = append(body, leb128.EncodeUint32(uint32(i))...) 71 } 72 73 body = append(body, wasm.OpcodeCall) 74 body = append(body, leb128.EncodeUint32(cnt)...) 75 body = append(body, wasm.OpcodeEnd) 76 proxyModule.CodeSection = append(proxyModule.CodeSection, wasm.Code{Body: body}) 77 78 proxyFuncIndex := cnt + funcNum 79 // Assigns the same params name as the imported one. 80 paramNames := wasm.NameMapAssoc{Index: proxyFuncIndex} 81 for i, n := range def.ParamNames() { 82 paramNames.NameMap = append(paramNames.NameMap, wasm.NameAssoc{Index: wasm.Index(i), Name: n}) 83 } 84 proxyModule.NameSection.LocalNames = append(proxyModule.NameSection.LocalNames, paramNames) 85 86 // Plus, assigns the same function name. 87 proxyModule.NameSection.FunctionNames = append(proxyModule.NameSection.FunctionNames, 88 wasm.NameAssoc{Index: proxyFuncIndex, Name: name}) 89 90 // Finally, exports the proxy function with the same name as the imported one. 91 proxyModule.ExportSection = append(proxyModule.ExportSection, wasm.Export{ 92 Type: wasm.ExternTypeFunc, 93 Name: name, 94 Index: proxyFuncIndex, 95 }) 96 cnt++ 97 } 98 return binaryencoding.EncodeModule(proxyModule) 99 }