package wasm
import (
"errors"
"fmt"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/internal/wasmdebug"
)
type HostFuncExporter interface {
ExportHostFunc (*HostFunc )
}
type HostFunc struct {
ExportName string
Name string
ParamTypes []ValueType
ParamNames []string
ResultTypes []ValueType
ResultNames []string
Code Code
}
func (f *HostFunc ) WithGoModuleFunc (fn api .GoModuleFunc ) *HostFunc {
ret := *f
ret .Code .GoFunc = fn
return &ret
}
func NewHostModule (
moduleName string ,
exportNames []string ,
nameToHostFunc map [string ]*HostFunc ,
enabledFeatures api .CoreFeatures ,
) (m *Module , err error ) {
if moduleName != "" {
m = &Module {NameSection : &NameSection {ModuleName : moduleName }}
} else {
return nil , errors .New ("a module name must not be empty" )
}
if exportCount := uint32 (len (nameToHostFunc )); exportCount > 0 {
m .ExportSection = make ([]Export , 0 , exportCount )
m .Exports = make (map [string ]*Export , exportCount )
if err = addFuncs (m , exportNames , nameToHostFunc , enabledFeatures ); err != nil {
return
}
}
m .IsHostModule = true
m .AssignModuleID ([]byte (fmt .Sprintf ("@@@@@@@@%p" , m )),
nil , false )
return
}
func addFuncs(
m *Module ,
exportNames []string ,
nameToHostFunc map [string ]*HostFunc ,
enabledFeatures api .CoreFeatures ,
) (err error ) {
if m .NameSection == nil {
m .NameSection = &NameSection {}
}
moduleName := m .NameSection .ModuleName
for _ , k := range exportNames {
hf := nameToHostFunc [k ]
if hf .Name == "" {
hf .Name = k
}
switch hf .Code .GoFunc .(type ) {
case api .GoModuleFunction , api .GoFunction :
continue
}
hf .ParamTypes , hf .ResultTypes , hf .Code , err = parseGoReflectFunc (hf .Code .GoFunc )
if err != nil {
return fmt .Errorf ("func[%s.%s] %w" , moduleName , k , err )
}
params := hf .ParamTypes
if paramNames := hf .ParamNames ; paramNames != nil {
if paramNamesLen := len (paramNames ); paramNamesLen != len (params ) {
return fmt .Errorf ("func[%s.%s] has %d params, but %d params names" , moduleName , k , paramNamesLen , len (params ))
}
}
results := hf .ResultTypes
if resultNames := hf .ResultNames ; resultNames != nil {
if resultNamesLen := len (resultNames ); resultNamesLen != len (results ) {
return fmt .Errorf ("func[%s.%s] has %d results, but %d results names" , moduleName , k , resultNamesLen , len (results ))
}
}
}
funcCount := uint32 (len (exportNames ))
m .NameSection .FunctionNames = make ([]NameAssoc , 0 , funcCount )
m .FunctionSection = make ([]Index , 0 , funcCount )
m .CodeSection = make ([]Code , 0 , funcCount )
idx := Index (0 )
for _ , name := range exportNames {
hf := nameToHostFunc [name ]
debugName := wasmdebug .FuncName (moduleName , name , idx )
typeIdx , typeErr := m .maybeAddType (hf .ParamTypes , hf .ResultTypes , enabledFeatures )
if typeErr != nil {
return fmt .Errorf ("func[%s] %v" , debugName , typeErr )
}
m .FunctionSection = append (m .FunctionSection , typeIdx )
m .CodeSection = append (m .CodeSection , hf .Code )
export := hf .ExportName
m .ExportSection = append (m .ExportSection , Export {Type : ExternTypeFunc , Name : export , Index : idx })
m .Exports [export ] = &m .ExportSection [len (m .ExportSection )-1 ]
m .NameSection .FunctionNames = append (m .NameSection .FunctionNames , NameAssoc {Index : idx , Name : hf .Name })
if len (hf .ParamNames ) > 0 {
localNames := NameMapAssoc {Index : idx }
for i , n := range hf .ParamNames {
localNames .NameMap = append (localNames .NameMap , NameAssoc {Index : Index (i ), Name : n })
}
m .NameSection .LocalNames = append (m .NameSection .LocalNames , localNames )
}
if len (hf .ResultNames ) > 0 {
resultNames := NameMapAssoc {Index : idx }
for i , n := range hf .ResultNames {
resultNames .NameMap = append (resultNames .NameMap , NameAssoc {Index : Index (i ), Name : n })
}
m .NameSection .ResultNames = append (m .NameSection .ResultNames , resultNames )
}
idx ++
}
return nil
}
func (m *Module ) maybeAddType (params , results []ValueType , enabledFeatures api .CoreFeatures ) (Index , error ) {
if len (results ) > 1 {
if err := enabledFeatures .RequireEnabled (api .CoreFeatureMultiValue ); err != nil {
return 0 , fmt .Errorf ("multiple result types invalid as %v" , err )
}
}
for i := range m .TypeSection {
t := &m .TypeSection [i ]
if t .EqualsSignature (params , results ) {
return Index (i ), nil
}
}
result := m .SectionElementCount (SectionIDType )
m .TypeSection = append (m .TypeSection , FunctionType {Params : params , Results : results })
return result , nil
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .