Files
wazero/internal/wasm/host.go
Takeshi Yoneda 7666a2a7f7 Disable comp cache for host modules (#949)
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
2022-12-21 14:08:30 +09:00

232 lines
6.9 KiB
Go

package wasm
import (
"fmt"
"sort"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/internal/wasmdebug"
)
type HostFuncExporter interface {
ExportHostFunc(*HostFunc)
}
// HostFunc is a function with an inlined type, used for NewHostModule.
// Any corresponding FunctionType will be reused or added to the Module.
type HostFunc struct {
// ExportNames is equivalent to the same method on api.FunctionDefinition.
ExportNames []string
// Name is equivalent to the same method on api.FunctionDefinition.
Name string
// ParamTypes is equivalent to the same method on api.FunctionDefinition.
ParamTypes []ValueType
// ParamNames is equivalent to the same method on api.FunctionDefinition.
ParamNames []string
// ResultTypes is equivalent to the same method on api.FunctionDefinition.
ResultTypes []ValueType
// ResultNames is equivalent to the same method on api.FunctionDefinition.
ResultNames []string
// Code is the equivalent function in the SectionIDCode.
Code *Code
}
// MustGoReflectFunc calls WithGoReflectFunc or panics on error.
func (f *HostFunc) MustGoReflectFunc(fn interface{}) *HostFunc {
if ret, err := f.WithGoReflectFunc(fn); err != nil {
panic(err)
} else {
return ret
}
}
// WithGoFunc returns a copy of the function, replacing its Code.GoFunc.
func (f *HostFunc) WithGoFunc(fn api.GoFunc) *HostFunc {
ret := *f
ret.Code = &Code{IsHostFunction: true, GoFunc: fn}
return &ret
}
// WithGoModuleFunc returns a copy of the function, replacing its Code.GoFunc.
func (f *HostFunc) WithGoModuleFunc(fn api.GoModuleFunc) *HostFunc {
ret := *f
ret.Code = &Code{IsHostFunction: true, GoFunc: fn}
return &ret
}
// WithGoReflectFunc returns a copy of the function, replacing its Code.GoFunc.
func (f *HostFunc) WithGoReflectFunc(fn interface{}) (*HostFunc, error) {
ret := *f
var err error
ret.ParamTypes, ret.ResultTypes, ret.Code, err = parseGoReflectFunc(fn)
return &ret, err
}
// WithWasm returns a copy of the function, replacing its Code.Body.
func (f *HostFunc) WithWasm(body []byte) *HostFunc {
ret := *f
ret.Code = &Code{IsHostFunction: true, Body: body}
if f.Code != nil {
ret.Code.LocalTypes = f.Code.LocalTypes
}
return &ret
}
type HostFuncNames struct {
Name string
ParamNames []string
ResultNames []string
}
// NewHostModule is defined internally for use in WASI tests and to keep the code size in the root directory small.
func NewHostModule(
moduleName string,
nameToGoFunc map[string]interface{},
funcToNames map[string]*HostFuncNames,
enabledFeatures api.CoreFeatures,
) (m *Module, err error) {
if moduleName != "" {
m = &Module{NameSection: &NameSection{ModuleName: moduleName}}
} else {
m = &Module{}
}
if exportCount := uint32(len(nameToGoFunc)); exportCount > 0 {
m.ExportSection = make([]*Export, 0, exportCount)
if err = addFuncs(m, nameToGoFunc, funcToNames, enabledFeatures); err != nil {
return
}
}
m.IsHostModule = true
m.AssignModuleID([]byte(fmt.Sprintf("%s:%v:%v", moduleName, nameToGoFunc, enabledFeatures)))
m.BuildFunctionDefinitions()
return
}
func addFuncs(
m *Module,
nameToGoFunc map[string]interface{},
funcToNames map[string]*HostFuncNames,
enabledFeatures api.CoreFeatures,
) (err error) {
if m.NameSection == nil {
m.NameSection = &NameSection{}
}
moduleName := m.NameSection.ModuleName
nameToFunc := make(map[string]*HostFunc, len(nameToGoFunc))
sortedExportNames := make([]string, len(nameToFunc))
for k := range nameToGoFunc {
sortedExportNames = append(sortedExportNames, k)
}
// Sort names for consistent iteration
sort.Strings(sortedExportNames)
funcNames := make([]string, len(nameToFunc))
for _, k := range sortedExportNames {
v := nameToGoFunc[k]
if hf, ok := v.(*HostFunc); ok {
nameToFunc[hf.Name] = hf
funcNames = append(funcNames, hf.Name)
} else { // reflection
params, results, code, ftErr := parseGoReflectFunc(v)
if ftErr != nil {
return fmt.Errorf("func[%s.%s] %w", moduleName, k, ftErr)
}
hf = &HostFunc{
ExportNames: []string{k},
Name: k,
ParamTypes: params,
ResultTypes: results,
Code: code,
}
// Assign names to the function, if they exist.
ns := funcToNames[k]
if name := ns.Name; name != "" {
hf.Name = ns.Name
}
if paramNames := ns.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))
}
hf.ParamNames = paramNames
}
if resultNames := ns.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))
}
hf.ResultNames = resultNames
}
nameToFunc[k] = hf
funcNames = append(funcNames, k)
}
}
funcCount := uint32(len(nameToFunc))
m.NameSection.FunctionNames = make([]*NameAssoc, 0, funcCount)
m.FunctionSection = make([]Index, 0, funcCount)
m.CodeSection = make([]*Code, 0, funcCount)
m.FunctionDefinitionSection = make([]*FunctionDefinition, 0, funcCount)
idx := Index(0)
for _, name := range funcNames {
hf := nameToFunc[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)
for _, export := range hf.ExportNames {
m.ExportSection = append(m.ExportSection, &Export{Type: ExternTypeFunc, Name: export, Index: idx})
}
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 {
// Guard >1.0 feature multi-value
if err := enabledFeatures.RequireEnabled(api.CoreFeatureMultiValue); err != nil {
return 0, fmt.Errorf("multiple result types invalid as %v", err)
}
}
for i, t := range m.TypeSection {
if t.EqualsSignature(params, results) {
return Index(i), nil
}
}
result := m.SectionElementCount(SectionIDType)
toAdd := &FunctionType{Params: params, Results: results}
m.TypeSection = append(m.TypeSection, toAdd)
return result, nil
}