Files
wazero/namespace.go
Crypt Keeper 57a705e594 Disallows nil context and fixes linters (#754)
staticcheck linters broke until recent golangci-lint. Now, normal
behaviour of enforcing no nil context works again. Ex.
```
assemblyscript/assemblyscript_example_test.go:16:25: SA1012: do not pass a nil Context, even if a function permits it; pass context.TODO if you are unsure about which Context to use (staticcheck)
	r := wazero.NewRuntime(nil)
```

Since default lint already checks for nil context, this removes our
permission of nil context args. The original reason we permitted nil is
no longer valid: we once allowed context to be stashed in config, and
removed that as it caused bugs. We forgot to undo allowing nil
explicitly.

Note: this doesn't particularly check in our code for nil context,
similar as we don't particularly check in our code for nil anything
else. End users should use linters as none of our parameters should be
nil anyway.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-08-19 14:52:50 +08:00

123 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.
//
// 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)
}