wa-lang.org/wazero@v1.0.2/namespace.go (about)

     1  package wazero
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"wa-lang.org/wazero/api"
     8  	internalsys "wa-lang.org/wazero/internal/sys"
     9  	"wa-lang.org/wazero/internal/wasm"
    10  	"wa-lang.org/wazero/sys"
    11  )
    12  
    13  // Namespace contains instantiated modules, which cannot conflict until they are closed.
    14  type Namespace interface {
    15  	// Module returns exports from an instantiated module in this namespace or nil if there aren't any.
    16  	Module(moduleName string) api.Module
    17  
    18  	// InstantiateModule instantiates the module namespace or errs if the configuration was invalid.
    19  	//
    20  	// Here's an example:
    21  	//	module, _ := n.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("prod"))
    22  	//
    23  	// While CompiledModule is pre-validated, there are a few situations which can cause an error:
    24  	//   - The module name is already in use.
    25  	//   - The module has a table element initializer that resolves to an index outside the Table minimum size.
    26  	//   - The module has a start function, and it failed to execute.
    27  	InstantiateModule(ctx context.Context, compiled CompiledModule, config ModuleConfig) (api.Module, error)
    28  
    29  	// CloseWithExitCode closes all modules initialized in this Namespace with the provided exit code.
    30  	// An error is returned if any module returns an error when closed.
    31  	//
    32  	// Here's an example:
    33  	//	n := r.NewNamespace(ctx)
    34  	//	defer n.CloseWithExitCode(ctx, 2) // This closes all modules in this Namespace.
    35  	//
    36  	//	Everything below here can be closed, but will anyway due to above.
    37  	//	_, _ = wasi_snapshot_preview1.InstantiateSnapshotPreview1(ctx, n)
    38  	//	mod, _ := n.InstantiateModuleFromBinary(ctx, wasm)
    39  	//
    40  	// See Closer
    41  	CloseWithExitCode(ctx context.Context, exitCode uint32) error
    42  
    43  	// Closer closes modules initialized in this Namespace by delegating to CloseWithExitCode with an exit code of zero.
    44  	//
    45  	// Here's an example:
    46  	//	n := r.NewNamespace(ctx)
    47  	//	defer n.Close(ctx) // This closes all modules in this Namespace.
    48  	api.Closer
    49  }
    50  
    51  // namespace allows decoupling of public interfaces from internal representation.
    52  type namespace struct {
    53  	store *wasm.Store
    54  	ns    *wasm.Namespace
    55  }
    56  
    57  // Module implements Namespace.Module.
    58  func (ns *namespace) Module(moduleName string) api.Module {
    59  	return ns.ns.Module(moduleName)
    60  }
    61  
    62  // InstantiateModule implements Namespace.InstantiateModule
    63  func (ns *namespace) InstantiateModule(
    64  	ctx context.Context,
    65  	compiled CompiledModule,
    66  	mConfig ModuleConfig,
    67  ) (mod api.Module, err error) {
    68  	code := compiled.(*compiledModule)
    69  	config := mConfig.(*moduleConfig)
    70  
    71  	var sysCtx *internalsys.Context
    72  	if sysCtx, err = config.toSysContext(); err != nil {
    73  		return
    74  	}
    75  
    76  	name := config.name
    77  	if name == "" && code.module.NameSection != nil && code.module.NameSection.ModuleName != "" {
    78  		name = code.module.NameSection.ModuleName
    79  	}
    80  
    81  	// Instantiate the module in the appropriate namespace.
    82  	mod, err = ns.store.Instantiate(ctx, ns.ns, code.module, name, sysCtx)
    83  	if err != nil {
    84  		// If there was an error, don't leak the compiled module.
    85  		if code.closeWithModule {
    86  			_ = code.Close(ctx) // don't overwrite the error
    87  		}
    88  		return
    89  	}
    90  
    91  	// Attach the code closer so that anything afterwards closes the compiled code when closing the module.
    92  	if code.closeWithModule {
    93  		mod.(*wasm.CallContext).CodeCloser = code
    94  	}
    95  
    96  	// Now, invoke any start functions, failing at first error.
    97  	for _, fn := range config.startFunctions {
    98  		start := mod.ExportedFunction(fn)
    99  		if start == nil {
   100  			continue
   101  		}
   102  		if _, err = start.Call(ctx); err != nil {
   103  			_ = mod.Close(ctx) // Don't leak the module on error.
   104  			if _, ok := err.(*sys.ExitError); ok {
   105  				return // Don't wrap an exit error
   106  			}
   107  			err = fmt.Errorf("module[%s] function[%s] failed: %w", name, fn, err)
   108  			return
   109  		}
   110  	}
   111  	return
   112  }
   113  
   114  // Close implements api.Closer embedded in Namespace.
   115  func (ns *namespace) Close(ctx context.Context) error {
   116  	return ns.CloseWithExitCode(ctx, 0)
   117  }
   118  
   119  // CloseWithExitCode implements Namespace.CloseWithExitCode
   120  func (ns *namespace) CloseWithExitCode(ctx context.Context, exitCode uint32) error {
   121  	return ns.ns.CloseWithExitCode(ctx, exitCode)
   122  }