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  }