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:
76
builder.go
76
builder.go
@@ -146,7 +146,7 @@ type HostFunctionBuilder interface {
|
|||||||
// defer r.Close(ctx) // This closes everything this Runtime created.
|
// defer r.Close(ctx) // This closes everything this Runtime created.
|
||||||
//
|
//
|
||||||
// hello := func() {
|
// hello := func() {
|
||||||
// fmt.Fprintln(stdout, "hello!")
|
// println("hello!")
|
||||||
// }
|
// }
|
||||||
// env, _ := r.NewHostModuleBuilder("env").
|
// env, _ := r.NewHostModuleBuilder("env").
|
||||||
// NewFunctionBuilder().WithFunc(hello).Export("hello").
|
// NewFunctionBuilder().WithFunc(hello).Export("hello").
|
||||||
@@ -170,8 +170,8 @@ type HostFunctionBuilder interface {
|
|||||||
// chaining.
|
// chaining.
|
||||||
// - methods do not return errors, to allow chaining. Any validation errors
|
// - methods do not return errors, to allow chaining. Any validation errors
|
||||||
// are deferred until Compile.
|
// are deferred until Compile.
|
||||||
// - Insertion order is not retained. Anything defined by this builder is
|
// - Functions are indexed in order of calls to NewFunctionBuilder as
|
||||||
// sorted lexicographically on Compile.
|
// insertion ordering is needed by ABI such as Emscripten (invoke_*).
|
||||||
type HostModuleBuilder interface {
|
type HostModuleBuilder interface {
|
||||||
// Note: until golang/go#5860, we can't use example tests to embed code in interface godocs.
|
// 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.
|
// defer r.Close(ctx) // This closes everything this Runtime created.
|
||||||
//
|
//
|
||||||
// hello := func() {
|
// hello := func() {
|
||||||
// fmt.Fprintln(stdout, "hello!")
|
// println("hello!")
|
||||||
// }
|
// }
|
||||||
// env, _ := r.NewHostModuleBuilder("env").
|
// env, _ := r.NewHostModuleBuilder("env").
|
||||||
// NewFunctionBuilder().WithFunc(hello).Export("hello").
|
// NewFunctionBuilder().WithFunc(hello).Export("hello").
|
||||||
@@ -207,19 +207,18 @@ type HostModuleBuilder interface {
|
|||||||
|
|
||||||
// hostModuleBuilder implements HostModuleBuilder
|
// hostModuleBuilder implements HostModuleBuilder
|
||||||
type hostModuleBuilder struct {
|
type hostModuleBuilder struct {
|
||||||
r *runtime
|
r *runtime
|
||||||
moduleName string
|
moduleName string
|
||||||
nameToGoFunc map[string]interface{}
|
exportNames []string
|
||||||
funcToNames map[string]*wasm.HostFuncNames
|
nameToHostFunc map[string]*wasm.HostFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHostModuleBuilder implements Runtime.NewHostModuleBuilder
|
// NewHostModuleBuilder implements Runtime.NewHostModuleBuilder
|
||||||
func (r *runtime) NewHostModuleBuilder(moduleName string) HostModuleBuilder {
|
func (r *runtime) NewHostModuleBuilder(moduleName string) HostModuleBuilder {
|
||||||
return &hostModuleBuilder{
|
return &hostModuleBuilder{
|
||||||
r: r,
|
r: r,
|
||||||
moduleName: moduleName,
|
moduleName: moduleName,
|
||||||
nameToGoFunc: map[string]interface{}{},
|
nameToHostFunc: map[string]*wasm.HostFunc{},
|
||||||
funcToNames: map[string]*wasm.HostFuncNames{},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,21 +233,13 @@ type hostFunctionBuilder struct {
|
|||||||
|
|
||||||
// WithGoFunction implements HostFunctionBuilder.WithGoFunction
|
// WithGoFunction implements HostFunctionBuilder.WithGoFunction
|
||||||
func (h *hostFunctionBuilder) WithGoFunction(fn api.GoFunction, params, results []api.ValueType) HostFunctionBuilder {
|
func (h *hostFunctionBuilder) WithGoFunction(fn api.GoFunction, params, results []api.ValueType) HostFunctionBuilder {
|
||||||
h.fn = &wasm.HostFunc{
|
h.fn = &wasm.HostFunc{ParamTypes: params, ResultTypes: results, Code: wasm.Code{GoFunc: fn}}
|
||||||
ParamTypes: params,
|
|
||||||
ResultTypes: results,
|
|
||||||
Code: wasm.Code{GoFunc: fn},
|
|
||||||
}
|
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithGoModuleFunction implements HostFunctionBuilder.WithGoModuleFunction
|
// WithGoModuleFunction implements HostFunctionBuilder.WithGoModuleFunction
|
||||||
func (h *hostFunctionBuilder) WithGoModuleFunction(fn api.GoModuleFunction, params, results []api.ValueType) HostFunctionBuilder {
|
func (h *hostFunctionBuilder) WithGoModuleFunction(fn api.GoModuleFunction, params, results []api.ValueType) HostFunctionBuilder {
|
||||||
h.fn = &wasm.HostFunc{
|
h.fn = &wasm.HostFunc{ParamTypes: params, ResultTypes: results, Code: wasm.Code{GoFunc: fn}}
|
||||||
ParamTypes: params,
|
|
||||||
ResultTypes: results,
|
|
||||||
Code: wasm.Code{GoFunc: fn},
|
|
||||||
}
|
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,30 +269,35 @@ func (h *hostFunctionBuilder) WithResultNames(names ...string) HostFunctionBuild
|
|||||||
|
|
||||||
// Export implements HostFunctionBuilder.Export
|
// Export implements HostFunctionBuilder.Export
|
||||||
func (h *hostFunctionBuilder) Export(exportName string) HostModuleBuilder {
|
func (h *hostFunctionBuilder) Export(exportName string) HostModuleBuilder {
|
||||||
if h.name == "" {
|
var hostFn *wasm.HostFunc
|
||||||
h.name = exportName
|
|
||||||
}
|
|
||||||
names := &wasm.HostFuncNames{
|
|
||||||
Name: h.name,
|
|
||||||
ParamNames: h.paramNames,
|
|
||||||
ResultNames: h.resultNames,
|
|
||||||
}
|
|
||||||
if fn, ok := h.fn.(*wasm.HostFunc); ok {
|
if fn, ok := h.fn.(*wasm.HostFunc); ok {
|
||||||
if fn.Name == "" {
|
hostFn = fn
|
||||||
fn.Name = names.Name
|
} else {
|
||||||
}
|
hostFn = &wasm.HostFunc{Code: wasm.Code{GoFunc: h.fn}}
|
||||||
fn.ParamNames = names.ParamNames
|
|
||||||
fn.ResultNames = names.ResultNames
|
|
||||||
fn.ExportNames = []string{exportName}
|
|
||||||
}
|
}
|
||||||
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
|
return h.b
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportHostFunc implements wasm.HostFuncExporter
|
// ExportHostFunc implements wasm.HostFuncExporter
|
||||||
func (b *hostModuleBuilder) ExportHostFunc(fn *wasm.HostFunc) {
|
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
|
// NewFunctionBuilder implements HostModuleBuilder.NewFunctionBuilder
|
||||||
@@ -311,7 +307,7 @@ func (b *hostModuleBuilder) NewFunctionBuilder() HostFunctionBuilder {
|
|||||||
|
|
||||||
// Compile implements HostModuleBuilder.Compile
|
// Compile implements HostModuleBuilder.Compile
|
||||||
func (b *hostModuleBuilder) Compile(ctx context.Context) (CompiledModule, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if err = module.Validate(b.r.enabledFeatures); err != nil {
|
} else if err = module.Validate(b.r.enabledFeatures); err != nil {
|
||||||
|
|||||||
@@ -159,21 +159,21 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: &wasm.Module{
|
expected: &wasm.Module{
|
||||||
TypeSection: []wasm.FunctionType{
|
TypeSection: []wasm.FunctionType{
|
||||||
{Params: []api.ValueType{i32}, Results: []api.ValueType{i32}},
|
|
||||||
{Params: []api.ValueType{i64}, Results: []api.ValueType{i32}},
|
{Params: []api.ValueType{i64}, Results: []api.ValueType{i32}},
|
||||||
|
{Params: []api.ValueType{i32}, Results: []api.ValueType{i32}},
|
||||||
},
|
},
|
||||||
FunctionSection: []wasm.Index{0, 1},
|
FunctionSection: []wasm.Index{0, 1},
|
||||||
CodeSection: []wasm.Code{wasm.MustParseGoReflectFuncCode(uint32_uint32), wasm.MustParseGoReflectFuncCode(uint64_uint32)},
|
CodeSection: []wasm.Code{wasm.MustParseGoReflectFuncCode(uint64_uint32), wasm.MustParseGoReflectFuncCode(uint32_uint32)},
|
||||||
ExportSection: []wasm.Export{
|
ExportSection: []wasm.Export{
|
||||||
{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
|
{Name: "2", Type: wasm.ExternTypeFunc, Index: 0},
|
||||||
{Name: "2", Type: wasm.ExternTypeFunc, Index: 1},
|
{Name: "1", Type: wasm.ExternTypeFunc, Index: 1},
|
||||||
},
|
},
|
||||||
Exports: map[string]*wasm.Export{
|
Exports: map[string]*wasm.Export{
|
||||||
"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
|
"2": {Name: "2", Type: wasm.ExternTypeFunc, Index: 0},
|
||||||
"2": {Name: "2", Type: wasm.ExternTypeFunc, Index: 1},
|
"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 1},
|
||||||
},
|
},
|
||||||
NameSection: &wasm.NameSection{
|
NameSection: &wasm.NameSection{
|
||||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}, {Index: 1, Name: "2"}},
|
FunctionNames: wasm.NameMap{{Index: 0, Name: "2"}, {Index: 1, Name: "1"}},
|
||||||
ModuleName: "host",
|
ModuleName: "host",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -269,7 +269,7 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "WithGoFunction twice",
|
name: "WithGoFunction twice",
|
||||||
input: func(r Runtime) HostModuleBuilder {
|
input: func(r Runtime) HostModuleBuilder {
|
||||||
// Intentionally out of order
|
// Intentionally not in lexicographic order
|
||||||
return r.NewHostModuleBuilder("host").
|
return r.NewHostModuleBuilder("host").
|
||||||
NewFunctionBuilder().
|
NewFunctionBuilder().
|
||||||
WithGoFunction(gofunc2, []api.ValueType{i64}, []api.ValueType{i32}).
|
WithGoFunction(gofunc2, []api.ValueType{i64}, []api.ValueType{i32}).
|
||||||
@@ -280,24 +280,24 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: &wasm.Module{
|
expected: &wasm.Module{
|
||||||
TypeSection: []wasm.FunctionType{
|
TypeSection: []wasm.FunctionType{
|
||||||
{Params: []api.ValueType{i32}, Results: []api.ValueType{i32}},
|
|
||||||
{Params: []api.ValueType{i64}, Results: []api.ValueType{i32}},
|
{Params: []api.ValueType{i64}, Results: []api.ValueType{i32}},
|
||||||
|
{Params: []api.ValueType{i32}, Results: []api.ValueType{i32}},
|
||||||
},
|
},
|
||||||
FunctionSection: []wasm.Index{0, 1},
|
FunctionSection: []wasm.Index{0, 1},
|
||||||
CodeSection: []wasm.Code{
|
CodeSection: []wasm.Code{
|
||||||
{GoFunc: gofunc1},
|
|
||||||
{GoFunc: gofunc2},
|
{GoFunc: gofunc2},
|
||||||
|
{GoFunc: gofunc1},
|
||||||
},
|
},
|
||||||
ExportSection: []wasm.Export{
|
ExportSection: []wasm.Export{
|
||||||
{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
|
{Name: "2", Type: wasm.ExternTypeFunc, Index: 0},
|
||||||
{Name: "2", Type: wasm.ExternTypeFunc, Index: 1},
|
{Name: "1", Type: wasm.ExternTypeFunc, Index: 1},
|
||||||
},
|
},
|
||||||
Exports: map[string]*wasm.Export{
|
Exports: map[string]*wasm.Export{
|
||||||
"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
|
"2": {Name: "2", Type: wasm.ExternTypeFunc, Index: 0},
|
||||||
"2": {Name: "2", Type: wasm.ExternTypeFunc, Index: 1},
|
"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 1},
|
||||||
},
|
},
|
||||||
NameSection: &wasm.NameSection{
|
NameSection: &wasm.NameSection{
|
||||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}, {Index: 1, Name: "2"}},
|
FunctionNames: wasm.NameMap{{Index: 0, Name: "2"}, {Index: 1, Name: "1"}},
|
||||||
ModuleName: "host",
|
ModuleName: "host",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -342,18 +342,13 @@ func TestNewHostModuleBuilder_Compile_Errors(t *testing.T) {
|
|||||||
expectedErr string
|
expectedErr string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "error compiling", // should fail due to missing result.
|
name: "error compiling", // should fail due to invalid param.
|
||||||
input: func(rt Runtime) HostModuleBuilder {
|
input: func(rt Runtime) HostModuleBuilder {
|
||||||
return rt.NewHostModuleBuilder("host").NewFunctionBuilder().
|
return rt.NewHostModuleBuilder("host").NewFunctionBuilder().
|
||||||
WithFunc(&wasm.HostFunc{
|
WithFunc(&wasm.HostFunc{ExportName: "fn", Code: wasm.Code{GoFunc: func(string) {}}}).
|
||||||
ExportNames: []string{"fn"},
|
Export("fn")
|
||||||
ResultTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
|
||||||
Code: wasm.Code{Body: []byte{wasm.OpcodeEnd}},
|
|
||||||
}).Export("fn")
|
|
||||||
},
|
},
|
||||||
expectedErr: `invalid function[0] export["fn"]: not enough results
|
expectedErr: `func[host.fn] param[0] is unsupported: string`,
|
||||||
have ()
|
|
||||||
want (i32)`,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -138,11 +138,11 @@ func (e *functionExporter) ExportFunctions(builder wazero.HostModuleBuilder) {
|
|||||||
//
|
//
|
||||||
// See https://github.com/AssemblyScript/assemblyscript/blob/v0.26.7/std/assembly/builtins.ts#L2508
|
// See https://github.com/AssemblyScript/assemblyscript/blob/v0.26.7/std/assembly/builtins.ts#L2508
|
||||||
var abortMessageEnabled = &wasm.HostFunc{
|
var abortMessageEnabled = &wasm.HostFunc{
|
||||||
ExportNames: []string{AbortName},
|
ExportName: AbortName,
|
||||||
Name: "~lib/builtins/abort",
|
Name: "~lib/builtins/abort",
|
||||||
ParamTypes: []api.ValueType{i32, i32, i32, i32},
|
ParamTypes: []api.ValueType{i32, i32, i32, i32},
|
||||||
ParamNames: []string{"message", "fileName", "lineNumber", "columnNumber"},
|
ParamNames: []string{"message", "fileName", "lineNumber", "columnNumber"},
|
||||||
Code: wasm.Code{GoFunc: api.GoModuleFunc(abortWithMessage)},
|
Code: wasm.Code{GoFunc: api.GoModuleFunc(abortWithMessage)},
|
||||||
}
|
}
|
||||||
|
|
||||||
var abortMessageDisabled = abortMessageEnabled.WithGoModuleFunc(abort)
|
var abortMessageDisabled = abortMessageEnabled.WithGoModuleFunc(abort)
|
||||||
@@ -185,10 +185,10 @@ var traceDisabled = traceStdout.WithGoModuleFunc(func(context.Context, api.Modul
|
|||||||
|
|
||||||
// traceStdout implements trace to the configured Stdout.
|
// traceStdout implements trace to the configured Stdout.
|
||||||
var traceStdout = &wasm.HostFunc{
|
var traceStdout = &wasm.HostFunc{
|
||||||
ExportNames: []string{TraceName},
|
ExportName: TraceName,
|
||||||
Name: "~lib/builtins/trace",
|
Name: "~lib/builtins/trace",
|
||||||
ParamTypes: []api.ValueType{i32, i32, f64, f64, f64, f64, f64},
|
ParamTypes: []api.ValueType{i32, i32, f64, f64, f64, f64, f64},
|
||||||
ParamNames: []string{"message", "nArgs", "arg0", "arg1", "arg2", "arg3", "arg4"},
|
ParamNames: []string{"message", "nArgs", "arg0", "arg1", "arg2", "arg3", "arg4"},
|
||||||
Code: wasm.Code{
|
Code: wasm.Code{
|
||||||
GoFunc: api.GoModuleFunc(func(_ context.Context, mod api.Module, stack []uint64) {
|
GoFunc: api.GoModuleFunc(func(_ context.Context, mod api.Module, stack []uint64) {
|
||||||
fsc := mod.(*wasm.ModuleInstance).Sys.FS()
|
fsc := mod.(*wasm.ModuleInstance).Sys.FS()
|
||||||
@@ -270,7 +270,7 @@ func formatFloat(f float64) string {
|
|||||||
//
|
//
|
||||||
// See https://github.com/AssemblyScript/assemblyscript/blob/v0.26.7/std/assembly/builtins.ts#L2531
|
// See https://github.com/AssemblyScript/assemblyscript/blob/v0.26.7/std/assembly/builtins.ts#L2531
|
||||||
var seed = &wasm.HostFunc{
|
var seed = &wasm.HostFunc{
|
||||||
ExportNames: []string{SeedName},
|
ExportName: SeedName,
|
||||||
Name: "~lib/builtins/seed",
|
Name: "~lib/builtins/seed",
|
||||||
ResultTypes: []api.ValueType{f64},
|
ResultTypes: []api.ValueType{f64},
|
||||||
ResultNames: []string{"rand"},
|
ResultNames: []string{"rand"},
|
||||||
|
|||||||
@@ -94,11 +94,10 @@ func (functionExporter) ExportFunctions(builder wazero.HostModuleBuilder) {
|
|||||||
const functionNotifyMemoryGrowth = "emscripten_notify_memory_growth"
|
const functionNotifyMemoryGrowth = "emscripten_notify_memory_growth"
|
||||||
|
|
||||||
var notifyMemoryGrowth = &wasm.HostFunc{
|
var notifyMemoryGrowth = &wasm.HostFunc{
|
||||||
ExportNames: []string{functionNotifyMemoryGrowth},
|
ExportName: functionNotifyMemoryGrowth,
|
||||||
Name: functionNotifyMemoryGrowth,
|
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
ParamNames: []string{"memory_index"},
|
||||||
ParamNames: []string{"memory_index"},
|
Code: wasm.Code{GoFunc: api.GoModuleFunc(func(context.Context, api.Module, []uint64) {})},
|
||||||
Code: wasm.Code{GoFunc: api.GoModuleFunc(func(context.Context, api.Module, []uint64) {})},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// All `invoke_` functions have an initial "index" parameter of
|
// All `invoke_` functions have an initial "index" parameter of
|
||||||
@@ -134,8 +133,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var invokeI = &wasm.HostFunc{
|
var invokeI = &wasm.HostFunc{
|
||||||
ExportNames: []string{functionInvokeI},
|
ExportName: functionInvokeI,
|
||||||
Name: functionInvokeI,
|
|
||||||
ParamTypes: []api.ValueType{i32},
|
ParamTypes: []api.ValueType{i32},
|
||||||
ParamNames: []string{"index"},
|
ParamNames: []string{"index"},
|
||||||
ResultTypes: []api.ValueType{i32},
|
ResultTypes: []api.ValueType{i32},
|
||||||
@@ -151,8 +149,7 @@ func invokeIFn(ctx context.Context, mod api.Module, stack []uint64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var invokeIi = &wasm.HostFunc{
|
var invokeIi = &wasm.HostFunc{
|
||||||
ExportNames: []string{functionInvokeIi},
|
ExportName: functionInvokeIi,
|
||||||
Name: functionInvokeIi,
|
|
||||||
ParamTypes: []api.ValueType{i32, i32},
|
ParamTypes: []api.ValueType{i32, i32},
|
||||||
ParamNames: []string{"index", "a1"},
|
ParamNames: []string{"index", "a1"},
|
||||||
ResultTypes: []api.ValueType{i32},
|
ResultTypes: []api.ValueType{i32},
|
||||||
@@ -168,8 +165,7 @@ func invokeIiFn(ctx context.Context, mod api.Module, stack []uint64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var invokeIii = &wasm.HostFunc{
|
var invokeIii = &wasm.HostFunc{
|
||||||
ExportNames: []string{functionInvokeIii},
|
ExportName: functionInvokeIii,
|
||||||
Name: functionInvokeIii,
|
|
||||||
ParamTypes: []api.ValueType{i32, i32, i32},
|
ParamTypes: []api.ValueType{i32, i32, i32},
|
||||||
ParamNames: []string{"index", "a1", "a2"},
|
ParamNames: []string{"index", "a1", "a2"},
|
||||||
ResultTypes: []api.ValueType{i32},
|
ResultTypes: []api.ValueType{i32},
|
||||||
@@ -185,8 +181,7 @@ func invokeIiiFn(ctx context.Context, mod api.Module, stack []uint64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var invokeIiii = &wasm.HostFunc{
|
var invokeIiii = &wasm.HostFunc{
|
||||||
ExportNames: []string{functionInvokeIiii},
|
ExportName: functionInvokeIiii,
|
||||||
Name: functionInvokeIiii,
|
|
||||||
ParamTypes: []api.ValueType{i32, i32, i32, i32},
|
ParamTypes: []api.ValueType{i32, i32, i32, i32},
|
||||||
ParamNames: []string{"index", "a1", "a2", "a3"},
|
ParamNames: []string{"index", "a1", "a2", "a3"},
|
||||||
ResultTypes: []api.ValueType{i32},
|
ResultTypes: []api.ValueType{i32},
|
||||||
@@ -202,8 +197,7 @@ func invokeIiiiFn(ctx context.Context, mod api.Module, stack []uint64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var invokeIiiii = &wasm.HostFunc{
|
var invokeIiiii = &wasm.HostFunc{
|
||||||
ExportNames: []string{functionInvokeIiiii},
|
ExportName: functionInvokeIiiii,
|
||||||
Name: functionInvokeIiiii,
|
|
||||||
ParamTypes: []api.ValueType{i32, i32, i32, i32, i32},
|
ParamTypes: []api.ValueType{i32, i32, i32, i32, i32},
|
||||||
ParamNames: []string{"index", "a1", "a2", "a3", "a4"},
|
ParamNames: []string{"index", "a1", "a2", "a3", "a4"},
|
||||||
ResultTypes: []api.ValueType{i32},
|
ResultTypes: []api.ValueType{i32},
|
||||||
@@ -219,8 +213,7 @@ func invokeIiiiiFn(ctx context.Context, mod api.Module, stack []uint64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var invokeV = &wasm.HostFunc{
|
var invokeV = &wasm.HostFunc{
|
||||||
ExportNames: []string{functionInvokeV},
|
ExportName: functionInvokeV,
|
||||||
Name: functionInvokeV,
|
|
||||||
ParamTypes: []api.ValueType{i32},
|
ParamTypes: []api.ValueType{i32},
|
||||||
ParamNames: []string{"index"},
|
ParamNames: []string{"index"},
|
||||||
ResultTypes: []api.ValueType{},
|
ResultTypes: []api.ValueType{},
|
||||||
@@ -235,8 +228,7 @@ func invokeVFn(ctx context.Context, mod api.Module, stack []uint64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var invokeVi = &wasm.HostFunc{
|
var invokeVi = &wasm.HostFunc{
|
||||||
ExportNames: []string{functionInvokeVi},
|
ExportName: functionInvokeVi,
|
||||||
Name: functionInvokeVi,
|
|
||||||
ParamTypes: []api.ValueType{i32, i32},
|
ParamTypes: []api.ValueType{i32, i32},
|
||||||
ParamNames: []string{"index", "a1"},
|
ParamNames: []string{"index", "a1"},
|
||||||
ResultTypes: []api.ValueType{},
|
ResultTypes: []api.ValueType{},
|
||||||
@@ -251,8 +243,7 @@ func invokeViFn(ctx context.Context, mod api.Module, stack []uint64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var invokeVii = &wasm.HostFunc{
|
var invokeVii = &wasm.HostFunc{
|
||||||
ExportNames: []string{functionInvokeVii},
|
ExportName: functionInvokeVii,
|
||||||
Name: functionInvokeVii,
|
|
||||||
ParamTypes: []api.ValueType{i32, i32, i32},
|
ParamTypes: []api.ValueType{i32, i32, i32},
|
||||||
ParamNames: []string{"index", "a1", "a2"},
|
ParamNames: []string{"index", "a1", "a2"},
|
||||||
ResultTypes: []api.ValueType{},
|
ResultTypes: []api.ValueType{},
|
||||||
@@ -267,8 +258,7 @@ func invokeViiFn(ctx context.Context, mod api.Module, stack []uint64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var invokeViii = &wasm.HostFunc{
|
var invokeViii = &wasm.HostFunc{
|
||||||
ExportNames: []string{functionInvokeViii},
|
ExportName: functionInvokeViii,
|
||||||
Name: functionInvokeViii,
|
|
||||||
ParamTypes: []api.ValueType{i32, i32, i32, i32},
|
ParamTypes: []api.ValueType{i32, i32, i32, i32},
|
||||||
ParamNames: []string{"index", "a1", "a2", "a3"},
|
ParamNames: []string{"index", "a1", "a2", "a3"},
|
||||||
ResultTypes: []api.ValueType{},
|
ResultTypes: []api.ValueType{},
|
||||||
@@ -283,8 +273,7 @@ func invokeViiiFn(ctx context.Context, mod api.Module, stack []uint64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var invokeViiii = &wasm.HostFunc{
|
var invokeViiii = &wasm.HostFunc{
|
||||||
ExportNames: []string{functionInvokeViiii},
|
ExportName: functionInvokeViiii,
|
||||||
Name: functionInvokeViiii,
|
|
||||||
ParamTypes: []api.ValueType{i32, i32, i32, i32, i32},
|
ParamTypes: []api.ValueType{i32, i32, i32, i32, i32},
|
||||||
ParamNames: []string{"index", "a1", "a2", "a3", "a4"},
|
ParamNames: []string{"index", "a1", "a2", "a3", "a4"},
|
||||||
ResultTypes: []api.ValueType{},
|
ResultTypes: []api.ValueType{},
|
||||||
|
|||||||
@@ -19,13 +19,10 @@ import (
|
|||||||
//
|
//
|
||||||
// See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#proc_exit
|
// See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#proc_exit
|
||||||
var procExit = &wasm.HostFunc{
|
var procExit = &wasm.HostFunc{
|
||||||
ExportNames: []string{wasip1.ProcExitName},
|
ExportName: wasip1.ProcExitName,
|
||||||
Name: wasip1.ProcExitName,
|
ParamTypes: []api.ValueType{i32},
|
||||||
ParamTypes: []api.ValueType{i32},
|
ParamNames: []string{"rval"},
|
||||||
ParamNames: []string{"rval"},
|
Code: wasm.Code{GoFunc: api.GoModuleFunc(procExitFn)},
|
||||||
Code: wasm.Code{
|
|
||||||
GoFunc: api.GoModuleFunc(procExitFn),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func procExitFn(ctx context.Context, mod api.Module, params []uint64) {
|
func procExitFn(ctx context.Context, mod api.Module, params []uint64) {
|
||||||
|
|||||||
@@ -263,8 +263,7 @@ func newHostFunc(
|
|||||||
paramNames ...string,
|
paramNames ...string,
|
||||||
) *wasm.HostFunc {
|
) *wasm.HostFunc {
|
||||||
return &wasm.HostFunc{
|
return &wasm.HostFunc{
|
||||||
ExportNames: []string{name},
|
ExportName: name,
|
||||||
Name: name,
|
|
||||||
ParamTypes: paramTypes,
|
ParamTypes: paramTypes,
|
||||||
ParamNames: paramNames,
|
ParamNames: paramNames,
|
||||||
ResultTypes: []api.ValueType{i32},
|
ResultTypes: []api.ValueType{i32},
|
||||||
@@ -291,8 +290,7 @@ func (f wasiFunc) Call(ctx context.Context, mod api.Module, stack []uint64) {
|
|||||||
// stubFunction stubs for GrainLang per #271.
|
// stubFunction stubs for GrainLang per #271.
|
||||||
func stubFunction(name string, paramTypes []wasm.ValueType, paramNames ...string) *wasm.HostFunc {
|
func stubFunction(name string, paramTypes []wasm.ValueType, paramNames ...string) *wasm.HostFunc {
|
||||||
return &wasm.HostFunc{
|
return &wasm.HostFunc{
|
||||||
Name: name,
|
ExportName: name,
|
||||||
ExportNames: []string{name},
|
|
||||||
ParamTypes: paramTypes,
|
ParamTypes: paramTypes,
|
||||||
ParamNames: paramNames,
|
ParamNames: paramNames,
|
||||||
ResultTypes: []api.ValueType{i32},
|
ResultTypes: []api.ValueType{i32},
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ func TestCompiler_SliceAllocatedOnHeap(t *testing.T) {
|
|||||||
|
|
||||||
const hostModuleName = "env"
|
const hostModuleName = "env"
|
||||||
const hostFnName = "grow_and_shrink_goroutine_stack"
|
const hostFnName = "grow_and_shrink_goroutine_stack"
|
||||||
hm, err := wasm.NewHostModule(hostModuleName, map[string]interface{}{hostFnName: func() {
|
hostFn := func() {
|
||||||
// This function aggressively grow the goroutine stack by recursively
|
// This function aggressively grow the goroutine stack by recursively
|
||||||
// calling the function many times.
|
// calling the function many times.
|
||||||
callNum := 1000
|
callNum := 1000
|
||||||
@@ -247,7 +247,13 @@ func TestCompiler_SliceAllocatedOnHeap(t *testing.T) {
|
|||||||
// Trigger relocation of goroutine stack because at this point we have the majority of
|
// Trigger relocation of goroutine stack because at this point we have the majority of
|
||||||
// goroutine stack unused after recursive call.
|
// goroutine stack unused after recursive call.
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
}}, map[string]*wasm.HostFuncNames{hostFnName: {}}, enabledFeatures)
|
}
|
||||||
|
hm, err := wasm.NewHostModule(
|
||||||
|
hostModuleName,
|
||||||
|
[]string{hostFnName},
|
||||||
|
map[string]*wasm.HostFunc{hostFnName: {ExportName: hostFnName, Code: wasm.Code{GoFunc: hostFn}}},
|
||||||
|
enabledFeatures,
|
||||||
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = s.Engine.CompileModule(testCtx, hm, nil, false)
|
err = s.Engine.CompileModule(testCtx, hm, nil, false)
|
||||||
|
|||||||
@@ -16,11 +16,10 @@ import (
|
|||||||
// This traps (unreachable opcode) to ensure the function is never called.
|
// This traps (unreachable opcode) to ensure the function is never called.
|
||||||
func StubFunction(name string) *wasm.HostFunc {
|
func StubFunction(name string) *wasm.HostFunc {
|
||||||
return &wasm.HostFunc{
|
return &wasm.HostFunc{
|
||||||
ExportNames: []string{name},
|
ExportName: name,
|
||||||
Name: name,
|
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
ParamNames: []string{"sp"},
|
||||||
ParamNames: []string{"sp"},
|
Code: wasm.Code{GoFunc: api.GoModuleFunc(func(ctx context.Context, _ api.Module, stack []uint64) {})},
|
||||||
Code: wasm.Code{GoFunc: api.GoModuleFunc(func(ctx context.Context, _ api.Module, stack []uint64) {})},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,11 +38,10 @@ func MustRead(mem api.Memory, funcName string, paramIdx int, offset, byteCount u
|
|||||||
|
|
||||||
func NewFunc(name string, goFunc api.GoModuleFunc) *wasm.HostFunc {
|
func NewFunc(name string, goFunc api.GoModuleFunc) *wasm.HostFunc {
|
||||||
return &wasm.HostFunc{
|
return &wasm.HostFunc{
|
||||||
ExportNames: []string{name},
|
ExportName: name,
|
||||||
Name: name,
|
ParamTypes: []api.ValueType{api.ValueTypeI32},
|
||||||
ParamTypes: []api.ValueType{api.ValueTypeI32},
|
ParamNames: []string{"sp"},
|
||||||
ParamNames: []string{"sp"},
|
Code: wasm.Code{GoFunc: goFunc},
|
||||||
Code: wasm.Code{GoFunc: goFunc},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,11 +58,21 @@ func RunTestEngine_MemoryGrowInRecursiveCall(t *testing.T, et EngineTester) {
|
|||||||
const hostModuleName = "env"
|
const hostModuleName = "env"
|
||||||
const hostFnName = "grow_memory"
|
const hostFnName = "grow_memory"
|
||||||
var growFn api.Function
|
var growFn api.Function
|
||||||
hm, err := wasm.NewHostModule(hostModuleName, map[string]interface{}{hostFnName: func() {
|
hm, err := wasm.NewHostModule(
|
||||||
// Does the recursive call into Wasm, which grows memory.
|
hostModuleName,
|
||||||
_, err := growFn.Call(context.Background())
|
[]string{hostFnName},
|
||||||
require.NoError(t, err)
|
map[string]*wasm.HostFunc{
|
||||||
}}, map[string]*wasm.HostFuncNames{hostFnName: {}}, enabledFeatures)
|
hostFnName: {
|
||||||
|
ExportName: hostFnName,
|
||||||
|
Code: wasm.Code{GoFunc: func() {
|
||||||
|
// Does the recursive call into Wasm, which grows memory.
|
||||||
|
_, err := growFn.Call(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
enabledFeatures,
|
||||||
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = s.Engine.CompileModule(testCtx, hm, nil, false)
|
err = s.Engine.CompileModule(testCtx, hm, nil, false)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package wasm
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
"github.com/tetratelabs/wazero/internal/wasmdebug"
|
"github.com/tetratelabs/wazero/internal/wasmdebug"
|
||||||
@@ -16,8 +15,8 @@ type HostFuncExporter interface {
|
|||||||
// HostFunc is a function with an inlined type, used for NewHostModule.
|
// HostFunc is a function with an inlined type, used for NewHostModule.
|
||||||
// Any corresponding FunctionType will be reused or added to the Module.
|
// Any corresponding FunctionType will be reused or added to the Module.
|
||||||
type HostFunc struct {
|
type HostFunc struct {
|
||||||
// ExportNames is equivalent to the same method on api.FunctionDefinition.
|
// ExportName is the only value returned by api.FunctionDefinition.
|
||||||
ExportNames []string
|
ExportName string
|
||||||
|
|
||||||
// Name is equivalent to the same method on api.FunctionDefinition.
|
// Name is equivalent to the same method on api.FunctionDefinition.
|
||||||
Name string
|
Name string
|
||||||
@@ -45,17 +44,11 @@ func (f *HostFunc) WithGoModuleFunc(fn api.GoModuleFunc) *HostFunc {
|
|||||||
return &ret
|
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.
|
// NewHostModule is defined internally for use in WASI tests and to keep the code size in the root directory small.
|
||||||
func NewHostModule(
|
func NewHostModule(
|
||||||
moduleName string,
|
moduleName string,
|
||||||
nameToGoFunc map[string]interface{},
|
exportNames []string,
|
||||||
funcToNames map[string]*HostFuncNames,
|
nameToHostFunc map[string]*HostFunc,
|
||||||
enabledFeatures api.CoreFeatures,
|
enabledFeatures api.CoreFeatures,
|
||||||
) (m *Module, err error) {
|
) (m *Module, err error) {
|
||||||
if moduleName != "" {
|
if moduleName != "" {
|
||||||
@@ -64,10 +57,10 @@ func NewHostModule(
|
|||||||
return nil, errors.New("a module name must not be empty")
|
return nil, errors.New("a module name must not be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
if exportCount := uint32(len(nameToGoFunc)); exportCount > 0 {
|
if exportCount := uint32(len(nameToHostFunc)); exportCount > 0 {
|
||||||
m.ExportSection = make([]Export, 0, exportCount)
|
m.ExportSection = make([]Export, 0, exportCount)
|
||||||
m.Exports = make(map[string]*Export, exportCount)
|
m.Exports = make(map[string]*Export, exportCount)
|
||||||
if err = addFuncs(m, nameToGoFunc, funcToNames, enabledFeatures); err != nil {
|
if err = addFuncs(m, exportNames, nameToHostFunc, enabledFeatures); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,73 +78,55 @@ func NewHostModule(
|
|||||||
|
|
||||||
func addFuncs(
|
func addFuncs(
|
||||||
m *Module,
|
m *Module,
|
||||||
nameToGoFunc map[string]interface{},
|
exportNames []string,
|
||||||
funcToNames map[string]*HostFuncNames,
|
nameToHostFunc map[string]*HostFunc,
|
||||||
enabledFeatures api.CoreFeatures,
|
enabledFeatures api.CoreFeatures,
|
||||||
) (err error) {
|
) (err error) {
|
||||||
if m.NameSection == nil {
|
if m.NameSection == nil {
|
||||||
m.NameSection = &NameSection{}
|
m.NameSection = &NameSection{}
|
||||||
}
|
}
|
||||||
moduleName := m.NameSection.ModuleName
|
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
|
for _, k := range exportNames {
|
||||||
sort.Strings(sortedExportNames)
|
hf := nameToHostFunc[k]
|
||||||
|
if hf.Name == "" {
|
||||||
|
hf.Name = k // default name to export name
|
||||||
|
}
|
||||||
|
switch hf.Code.GoFunc.(type) {
|
||||||
|
case api.GoModuleFunction, api.GoFunction:
|
||||||
|
continue // already parsed
|
||||||
|
}
|
||||||
|
|
||||||
funcNames := make([]string, len(nameToFunc))
|
// Resolve the code using reflection
|
||||||
for _, k := range sortedExportNames {
|
hf.ParamTypes, hf.ResultTypes, hf.Code, err = parseGoReflectFunc(hf.Code.GoFunc)
|
||||||
v := nameToGoFunc[k]
|
if err != nil {
|
||||||
if hf, ok := v.(*HostFunc); ok {
|
return fmt.Errorf("func[%s.%s] %w", moduleName, k, err)
|
||||||
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.
|
// Assign names to the function, if they exist.
|
||||||
ns := funcToNames[k]
|
params := hf.ParamTypes
|
||||||
if name := ns.Name; name != "" {
|
if paramNames := hf.ParamNames; paramNames != nil {
|
||||||
hf.Name = ns.Name
|
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))
|
||||||
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
|
results := hf.ResultTypes
|
||||||
funcNames = append(funcNames, k)
|
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(nameToFunc))
|
funcCount := uint32(len(exportNames))
|
||||||
m.NameSection.FunctionNames = make([]NameAssoc, 0, funcCount)
|
m.NameSection.FunctionNames = make([]NameAssoc, 0, funcCount)
|
||||||
m.FunctionSection = make([]Index, 0, funcCount)
|
m.FunctionSection = make([]Index, 0, funcCount)
|
||||||
m.CodeSection = make([]Code, 0, funcCount)
|
m.CodeSection = make([]Code, 0, funcCount)
|
||||||
|
|
||||||
idx := Index(0)
|
idx := Index(0)
|
||||||
for _, name := range funcNames {
|
for _, name := range exportNames {
|
||||||
hf := nameToFunc[name]
|
hf := nameToHostFunc[name]
|
||||||
debugName := wasmdebug.FuncName(moduleName, name, idx)
|
debugName := wasmdebug.FuncName(moduleName, name, idx)
|
||||||
typeIdx, typeErr := m.maybeAddType(hf.ParamTypes, hf.ResultTypes, enabledFeatures)
|
typeIdx, typeErr := m.maybeAddType(hf.ParamTypes, hf.ResultTypes, enabledFeatures)
|
||||||
if typeErr != nil {
|
if typeErr != nil {
|
||||||
@@ -159,10 +134,10 @@ func addFuncs(
|
|||||||
}
|
}
|
||||||
m.FunctionSection = append(m.FunctionSection, typeIdx)
|
m.FunctionSection = append(m.FunctionSection, typeIdx)
|
||||||
m.CodeSection = append(m.CodeSection, hf.Code)
|
m.CodeSection = append(m.CodeSection, hf.Code)
|
||||||
for _, export := range hf.ExportNames {
|
|
||||||
m.ExportSection = append(m.ExportSection, Export{Type: ExternTypeFunc, Name: export, Index: idx})
|
export := hf.ExportName
|
||||||
m.Exports[export] = &m.ExportSection[len(m.ExportSection)-1]
|
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})
|
m.NameSection.FunctionNames = append(m.NameSection.FunctionNames, NameAssoc{Index: idx, Name: hf.Name})
|
||||||
|
|
||||||
if len(hf.ParamNames) > 0 {
|
if len(hf.ParamNames) > 0 {
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ func TestNewHostModule(t *testing.T) {
|
|||||||
swapName := "swap"
|
swapName := "swap"
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name, moduleName string
|
name, moduleName string
|
||||||
nameToGoFunc map[string]interface{}
|
exportNames []string
|
||||||
funcToNames map[string]*HostFuncNames
|
nameToHostFunc map[string]*HostFunc
|
||||||
expected *Module
|
expected *Module
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@@ -40,22 +40,21 @@ func TestNewHostModule(t *testing.T) {
|
|||||||
expected: &Module{NameSection: &NameSection{ModuleName: "test"}},
|
expected: &Module{NameSection: &NameSection{ModuleName: "test"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "funcs",
|
name: "funcs",
|
||||||
moduleName: InternalModuleName,
|
moduleName: InternalModuleName,
|
||||||
nameToGoFunc: map[string]interface{}{
|
exportNames: []string{ArgsSizesGetName, FdWriteName},
|
||||||
ArgsSizesGetName: argsSizesGet,
|
nameToHostFunc: map[string]*HostFunc{
|
||||||
FdWriteName: fdWrite,
|
|
||||||
},
|
|
||||||
funcToNames: map[string]*HostFuncNames{
|
|
||||||
ArgsSizesGetName: {
|
ArgsSizesGetName: {
|
||||||
Name: ArgsSizesGetName,
|
ExportName: ArgsSizesGetName,
|
||||||
ParamNames: []string{"result.argc", "result.argv_len"},
|
ParamNames: []string{"result.argc", "result.argv_len"},
|
||||||
ResultNames: []string{"errno"},
|
ResultNames: []string{"errno"},
|
||||||
|
Code: Code{GoFunc: argsSizesGet},
|
||||||
},
|
},
|
||||||
FdWriteName: {
|
FdWriteName: {
|
||||||
Name: FdWriteName,
|
ExportName: FdWriteName,
|
||||||
ParamNames: []string{"fd", "iovs", "iovs_len", "result.size"},
|
ParamNames: []string{"fd", "iovs", "iovs_len", "result.size"},
|
||||||
ResultNames: []string{"errno"},
|
ResultNames: []string{"errno"},
|
||||||
|
Code: Code{GoFunc: fdWrite},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: &Module{
|
expected: &Module{
|
||||||
@@ -99,12 +98,10 @@ func TestNewHostModule(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "multi-value",
|
name: "multi-value",
|
||||||
moduleName: "swapper",
|
moduleName: "swapper",
|
||||||
nameToGoFunc: map[string]interface{}{
|
exportNames: []string{swapName},
|
||||||
swapName: swap,
|
nameToHostFunc: map[string]*HostFunc{swapName: {ExportName: swapName, Code: Code{GoFunc: swap}}},
|
||||||
},
|
|
||||||
funcToNames: map[string]*HostFuncNames{swapName: {}},
|
|
||||||
expected: &Module{
|
expected: &Module{
|
||||||
TypeSection: []FunctionType{{Params: []ValueType{i32, i32}, Results: []ValueType{i32, i32}}},
|
TypeSection: []FunctionType{{Params: []ValueType{i32, i32}, Results: []ValueType{i32, i32}}},
|
||||||
FunctionSection: []Index{0},
|
FunctionSection: []Index{0},
|
||||||
@@ -120,7 +117,7 @@ func TestNewHostModule(t *testing.T) {
|
|||||||
tc := tt
|
tc := tt
|
||||||
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
m, e := NewHostModule(tc.moduleName, tc.nameToGoFunc, tc.funcToNames, api.CoreFeaturesV2)
|
m, e := NewHostModule(tc.moduleName, tc.exportNames, tc.nameToHostFunc, api.CoreFeaturesV2)
|
||||||
require.NoError(t, e)
|
require.NoError(t, e)
|
||||||
requireHostModuleEquals(t, tc.expected, m)
|
requireHostModuleEquals(t, tc.expected, m)
|
||||||
require.True(t, m.IsHostModule)
|
require.True(t, m.IsHostModule)
|
||||||
@@ -158,23 +155,23 @@ func requireHostModuleEquals(t *testing.T, expected, actual *Module) {
|
|||||||
func TestNewHostModule_Errors(t *testing.T) {
|
func TestNewHostModule_Errors(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name, moduleName string
|
name, moduleName string
|
||||||
nameToGoFunc map[string]interface{}
|
exportNames []string
|
||||||
funcToNames map[string]*HostFuncNames
|
nameToHostFunc map[string]*HostFunc
|
||||||
expectedErr string
|
expectedErr string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "not a function",
|
name: "not a function",
|
||||||
moduleName: "modname",
|
moduleName: "modname",
|
||||||
nameToGoFunc: map[string]interface{}{"fn": t},
|
exportNames: []string{"fn"},
|
||||||
funcToNames: map[string]*HostFuncNames{"fn": {}},
|
nameToHostFunc: map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: t}}},
|
||||||
expectedErr: "func[modname.fn] kind != func: ptr",
|
expectedErr: "func[modname.fn] kind != func: ptr",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "function has multiple results",
|
name: "function has multiple results",
|
||||||
moduleName: "yetanother",
|
moduleName: "yetanother",
|
||||||
nameToGoFunc: map[string]interface{}{"fn": func() (uint32, uint32) { return 0, 0 }},
|
exportNames: []string{"fn"},
|
||||||
funcToNames: map[string]*HostFuncNames{"fn": {}},
|
nameToHostFunc: map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: func() (uint32, uint32) { return 0, 0 }}}},
|
||||||
expectedErr: "func[yetanother.fn] multiple result types invalid as feature \"multi-value\" is disabled",
|
expectedErr: "func[yetanother.fn] multiple result types invalid as feature \"multi-value\" is disabled",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +179,7 @@ func TestNewHostModule_Errors(t *testing.T) {
|
|||||||
tc := tt
|
tc := tt
|
||||||
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
_, e := NewHostModule(tc.moduleName, tc.nameToGoFunc, tc.funcToNames, api.CoreFeaturesV1)
|
_, e := NewHostModule(tc.moduleName, tc.exportNames, tc.nameToHostFunc, api.CoreFeaturesV1)
|
||||||
require.EqualError(t, e, tc.expectedErr)
|
require.EqualError(t, e, tc.expectedErr)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,12 @@ func TestNewStore(t *testing.T) {
|
|||||||
|
|
||||||
func TestStore_Instantiate(t *testing.T) {
|
func TestStore_Instantiate(t *testing.T) {
|
||||||
s := newStore()
|
s := newStore()
|
||||||
m, err := NewHostModule("foo", map[string]interface{}{"fn": func() {}}, map[string]*HostFuncNames{"fn": {}}, api.CoreFeaturesV1)
|
m, err := NewHostModule(
|
||||||
|
"foo",
|
||||||
|
[]string{"fn"},
|
||||||
|
map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: func() {}}}},
|
||||||
|
api.CoreFeaturesV1,
|
||||||
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
sysCtx := sys.DefaultContext(nil)
|
sysCtx := sys.DefaultContext(nil)
|
||||||
@@ -184,7 +189,12 @@ func TestStore_CloseWithExitCode(t *testing.T) {
|
|||||||
func TestStore_hammer(t *testing.T) {
|
func TestStore_hammer(t *testing.T) {
|
||||||
const importedModuleName = "imported"
|
const importedModuleName = "imported"
|
||||||
|
|
||||||
m, err := NewHostModule(importedModuleName, map[string]interface{}{"fn": func() {}}, map[string]*HostFuncNames{"fn": {}}, api.CoreFeaturesV1)
|
m, err := NewHostModule(
|
||||||
|
importedModuleName,
|
||||||
|
[]string{"fn"},
|
||||||
|
map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: func() {}}}},
|
||||||
|
api.CoreFeaturesV1,
|
||||||
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s := newStore()
|
s := newStore()
|
||||||
@@ -239,7 +249,12 @@ func TestStore_hammer(t *testing.T) {
|
|||||||
func TestStore_hammer_close(t *testing.T) {
|
func TestStore_hammer_close(t *testing.T) {
|
||||||
const importedModuleName = "imported"
|
const importedModuleName = "imported"
|
||||||
|
|
||||||
m, err := NewHostModule(importedModuleName, map[string]interface{}{"fn": func() {}}, map[string]*HostFuncNames{"fn": {}}, api.CoreFeaturesV1)
|
m, err := NewHostModule(
|
||||||
|
importedModuleName,
|
||||||
|
[]string{"fn"},
|
||||||
|
map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: func() {}}}},
|
||||||
|
api.CoreFeaturesV1,
|
||||||
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s := newStore()
|
s := newStore()
|
||||||
@@ -299,7 +314,12 @@ func TestStore_Instantiate_Errors(t *testing.T) {
|
|||||||
const importedModuleName = "imported"
|
const importedModuleName = "imported"
|
||||||
const importingModuleName = "test"
|
const importingModuleName = "test"
|
||||||
|
|
||||||
m, err := NewHostModule(importedModuleName, map[string]interface{}{"fn": func() {}}, map[string]*HostFuncNames{"fn": {}}, api.CoreFeaturesV1)
|
m, err := NewHostModule(
|
||||||
|
importedModuleName,
|
||||||
|
[]string{"fn"},
|
||||||
|
map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: func() {}}}},
|
||||||
|
api.CoreFeaturesV1,
|
||||||
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("Fails if module name already in use", func(t *testing.T) {
|
t.Run("Fails if module name already in use", func(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user