Makes host function index insertion order (#1370)

This makes host functions index consistently on insertion order, rather
than lexicographic order. This helps with ABI such as Emscripten, which
need an expected order.

This also constrains the internal code around host functions to only one
export name. More than one was never used. By restricting this, logic is
simpler and smaller.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2023-04-18 00:18:11 +08:00
committed by GitHub
parent 27b405f17b
commit b9e03dc691
13 changed files with 208 additions and 227 deletions

View File

@@ -146,7 +146,7 @@ type HostFunctionBuilder interface {
// defer r.Close(ctx) // This closes everything this Runtime created.
//
// hello := func() {
// fmt.Fprintln(stdout, "hello!")
// println("hello!")
// }
// env, _ := r.NewHostModuleBuilder("env").
// NewFunctionBuilder().WithFunc(hello).Export("hello").
@@ -170,8 +170,8 @@ type HostFunctionBuilder interface {
// chaining.
// - methods do not return errors, to allow chaining. Any validation errors
// are deferred until Compile.
// - Insertion order is not retained. Anything defined by this builder is
// sorted lexicographically on Compile.
// - Functions are indexed in order of calls to NewFunctionBuilder as
// insertion ordering is needed by ABI such as Emscripten (invoke_*).
type HostModuleBuilder interface {
// Note: until golang/go#5860, we can't use example tests to embed code in interface godocs.
@@ -191,7 +191,7 @@ type HostModuleBuilder interface {
// defer r.Close(ctx) // This closes everything this Runtime created.
//
// hello := func() {
// fmt.Fprintln(stdout, "hello!")
// println("hello!")
// }
// env, _ := r.NewHostModuleBuilder("env").
// NewFunctionBuilder().WithFunc(hello).Export("hello").
@@ -207,19 +207,18 @@ type HostModuleBuilder interface {
// hostModuleBuilder implements HostModuleBuilder
type hostModuleBuilder struct {
r *runtime
moduleName string
nameToGoFunc map[string]interface{}
funcToNames map[string]*wasm.HostFuncNames
r *runtime
moduleName string
exportNames []string
nameToHostFunc map[string]*wasm.HostFunc
}
// NewHostModuleBuilder implements Runtime.NewHostModuleBuilder
func (r *runtime) NewHostModuleBuilder(moduleName string) HostModuleBuilder {
return &hostModuleBuilder{
r: r,
moduleName: moduleName,
nameToGoFunc: map[string]interface{}{},
funcToNames: map[string]*wasm.HostFuncNames{},
r: r,
moduleName: moduleName,
nameToHostFunc: map[string]*wasm.HostFunc{},
}
}
@@ -234,21 +233,13 @@ type hostFunctionBuilder struct {
// WithGoFunction implements HostFunctionBuilder.WithGoFunction
func (h *hostFunctionBuilder) WithGoFunction(fn api.GoFunction, params, results []api.ValueType) HostFunctionBuilder {
h.fn = &wasm.HostFunc{
ParamTypes: params,
ResultTypes: results,
Code: wasm.Code{GoFunc: fn},
}
h.fn = &wasm.HostFunc{ParamTypes: params, ResultTypes: results, Code: wasm.Code{GoFunc: fn}}
return h
}
// WithGoModuleFunction implements HostFunctionBuilder.WithGoModuleFunction
func (h *hostFunctionBuilder) WithGoModuleFunction(fn api.GoModuleFunction, params, results []api.ValueType) HostFunctionBuilder {
h.fn = &wasm.HostFunc{
ParamTypes: params,
ResultTypes: results,
Code: wasm.Code{GoFunc: fn},
}
h.fn = &wasm.HostFunc{ParamTypes: params, ResultTypes: results, Code: wasm.Code{GoFunc: fn}}
return h
}
@@ -278,30 +269,35 @@ func (h *hostFunctionBuilder) WithResultNames(names ...string) HostFunctionBuild
// Export implements HostFunctionBuilder.Export
func (h *hostFunctionBuilder) Export(exportName string) HostModuleBuilder {
if h.name == "" {
h.name = exportName
}
names := &wasm.HostFuncNames{
Name: h.name,
ParamNames: h.paramNames,
ResultNames: h.resultNames,
}
var hostFn *wasm.HostFunc
if fn, ok := h.fn.(*wasm.HostFunc); ok {
if fn.Name == "" {
fn.Name = names.Name
}
fn.ParamNames = names.ParamNames
fn.ResultNames = names.ResultNames
fn.ExportNames = []string{exportName}
hostFn = fn
} else {
hostFn = &wasm.HostFunc{Code: wasm.Code{GoFunc: h.fn}}
}
h.b.nameToGoFunc[exportName] = h.fn
h.b.funcToNames[exportName] = names
// Assign any names from the builder
hostFn.ExportName = exportName
if h.name != "" {
hostFn.Name = h.name
}
if len(h.paramNames) != 0 {
hostFn.ParamNames = h.paramNames
}
if len(h.resultNames) != 0 {
hostFn.ResultNames = h.resultNames
}
h.b.ExportHostFunc(hostFn)
return h.b
}
// ExportHostFunc implements wasm.HostFuncExporter
func (b *hostModuleBuilder) ExportHostFunc(fn *wasm.HostFunc) {
b.nameToGoFunc[fn.ExportNames[0]] = fn
if _, ok := b.nameToHostFunc[fn.ExportName]; !ok { // add a new name
b.exportNames = append(b.exportNames, fn.ExportName)
}
b.nameToHostFunc[fn.ExportName] = fn
}
// NewFunctionBuilder implements HostModuleBuilder.NewFunctionBuilder
@@ -311,7 +307,7 @@ func (b *hostModuleBuilder) NewFunctionBuilder() HostFunctionBuilder {
// Compile implements HostModuleBuilder.Compile
func (b *hostModuleBuilder) Compile(ctx context.Context) (CompiledModule, error) {
module, err := wasm.NewHostModule(b.moduleName, b.nameToGoFunc, b.funcToNames, b.r.enabledFeatures)
module, err := wasm.NewHostModule(b.moduleName, b.exportNames, b.nameToHostFunc, b.r.enabledFeatures)
if err != nil {
return nil, err
} else if err = module.Validate(b.r.enabledFeatures); err != nil {