This simplifies FunctionListener definition by making it possible to implement both interfaces without intermediate state. Passing the function definition to the before/after callbacks is the key. This also continues efforts towards Go 1.19 doc formatting. Signed-off-by: Adrian Cole <adrian@tetrate.io>
124 lines
4.0 KiB
Go
124 lines
4.0 KiB
Go
package wazero
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/tetratelabs/wazero/api"
|
|
internalsys "github.com/tetratelabs/wazero/internal/sys"
|
|
"github.com/tetratelabs/wazero/internal/wasm"
|
|
"github.com/tetratelabs/wazero/sys"
|
|
)
|
|
|
|
// Namespace contains instantiated modules, which cannot conflict until they are closed.
|
|
type Namespace interface {
|
|
// Module returns exports from an instantiated module in this namespace or nil if there aren't any.
|
|
Module(moduleName string) api.Module
|
|
|
|
// InstantiateModule instantiates the module namespace or errs if the configuration was invalid.
|
|
// When the context is nil, it defaults to context.Background.
|
|
//
|
|
// Ex.
|
|
// module, _ := n.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("prod"))
|
|
//
|
|
// While CompiledModule is pre-validated, there are a few situations which can cause an error:
|
|
// - The module name is already in use.
|
|
// - The module has a table element initializer that resolves to an index outside the Table minimum size.
|
|
// - The module has a start function, and it failed to execute.
|
|
InstantiateModule(ctx context.Context, compiled CompiledModule, config ModuleConfig) (api.Module, error)
|
|
|
|
// CloseWithExitCode closes all modules initialized in this Namespace with the provided exit code.
|
|
// An error is returned if any module returns an error when closed.
|
|
//
|
|
// Ex.
|
|
// n := r.NewNamespace(ctx)
|
|
// defer n.CloseWithExitCode(ctx, 2) // This closes all modules in this Namespace.
|
|
//
|
|
// Everything below here can be closed, but will anyway due to above.
|
|
// _, _ = wasi_snapshot_preview1.InstantiateSnapshotPreview1(ctx, n)
|
|
// mod, _ := n.InstantiateModuleFromBinary(ctx, wasm)
|
|
//
|
|
// See Closer
|
|
CloseWithExitCode(ctx context.Context, exitCode uint32) error
|
|
|
|
// Closer closes modules initialized in this Namespace by delegating to CloseWithExitCode with an exit code of zero.
|
|
//
|
|
// Ex.
|
|
// n := r.NewNamespace(ctx)
|
|
// defer n.Close(ctx) // This closes all modules in this Namespace.
|
|
api.Closer
|
|
}
|
|
|
|
// namespace allows decoupling of public interfaces from internal representation.
|
|
type namespace struct {
|
|
store *wasm.Store
|
|
ns *wasm.Namespace
|
|
}
|
|
|
|
// Module implements Namespace.Module.
|
|
func (ns *namespace) Module(moduleName string) api.Module {
|
|
return ns.ns.Module(moduleName)
|
|
}
|
|
|
|
// InstantiateModule implements Namespace.InstantiateModule
|
|
func (ns *namespace) InstantiateModule(
|
|
ctx context.Context,
|
|
compiled CompiledModule,
|
|
mConfig ModuleConfig,
|
|
) (mod api.Module, err error) {
|
|
code := compiled.(*compiledModule)
|
|
config := mConfig.(*moduleConfig)
|
|
|
|
var sysCtx *internalsys.Context
|
|
if sysCtx, err = config.toSysContext(); err != nil {
|
|
return
|
|
}
|
|
|
|
name := config.name
|
|
if name == "" && code.module.NameSection != nil && code.module.NameSection.ModuleName != "" {
|
|
name = code.module.NameSection.ModuleName
|
|
}
|
|
|
|
// Instantiate the module in the appropriate namespace.
|
|
mod, err = ns.store.Instantiate(ctx, ns.ns, code.module, name, sysCtx, code.listeners)
|
|
if err != nil {
|
|
// If there was an error, don't leak the compiled module.
|
|
if code.closeWithModule {
|
|
_ = code.Close(ctx) // don't overwrite the error
|
|
}
|
|
return
|
|
}
|
|
|
|
// Attach the code closer so that anything afterwards closes the compiled code when closing the module.
|
|
if code.closeWithModule {
|
|
mod.(*wasm.CallContext).CodeCloser = code
|
|
}
|
|
|
|
// Now, invoke any start functions, failing at first error.
|
|
for _, fn := range config.startFunctions {
|
|
start := mod.ExportedFunction(fn)
|
|
if start == nil {
|
|
continue
|
|
}
|
|
if _, err = start.Call(ctx); err != nil {
|
|
_ = mod.Close(ctx) // Don't leak the module on error.
|
|
if _, ok := err.(*sys.ExitError); ok {
|
|
return // Don't wrap an exit error
|
|
}
|
|
err = fmt.Errorf("module[%s] function[%s] failed: %w", name, fn, err)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Close implements api.Closer embedded in Namespace.
|
|
func (ns *namespace) Close(ctx context.Context) error {
|
|
return ns.CloseWithExitCode(ctx, 0)
|
|
}
|
|
|
|
// CloseWithExitCode implements Namespace.CloseWithExitCode
|
|
func (ns *namespace) CloseWithExitCode(ctx context.Context, exitCode uint32) error {
|
|
return ns.ns.CloseWithExitCode(ctx, exitCode)
|
|
}
|