Files
wazero/namespace.go
Crypt Keeper 939403c10b Makes it possible to implement FunctionListener and Factory (#716)
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>
2022-07-25 12:53:41 +08:00

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)
}