package wazero
import (
"context"
"fmt"
"sync/atomic"
"github.com/tetratelabs/wazero/api"
experimentalapi "github.com/tetratelabs/wazero/experimental"
"github.com/tetratelabs/wazero/internal/engine/interpreter"
"github.com/tetratelabs/wazero/internal/engine/wazevo"
"github.com/tetratelabs/wazero/internal/expctxkeys"
"github.com/tetratelabs/wazero/internal/platform"
internalsock "github.com/tetratelabs/wazero/internal/sock"
internalsys "github.com/tetratelabs/wazero/internal/sys"
"github.com/tetratelabs/wazero/internal/wasm"
binaryformat "github.com/tetratelabs/wazero/internal/wasm/binary"
"github.com/tetratelabs/wazero/sys"
)
type Runtime interface {
Instantiate (ctx context .Context , source []byte ) (api .Module , error )
InstantiateWithConfig (ctx context .Context , source []byte , config ModuleConfig ) (api .Module , error )
NewHostModuleBuilder (moduleName string ) HostModuleBuilder
CompileModule (ctx context .Context , binary []byte ) (CompiledModule , error )
InstantiateModule (ctx context .Context , compiled CompiledModule , config ModuleConfig ) (api .Module , error )
CloseWithExitCode (ctx context .Context , exitCode uint32 ) error
Module (moduleName string ) api .Module
api .Closer
}
func NewRuntime (ctx context .Context ) Runtime {
return NewRuntimeWithConfig (ctx , NewRuntimeConfig ())
}
func NewRuntimeWithConfig (ctx context .Context , rConfig RuntimeConfig ) Runtime {
config := rConfig .(*runtimeConfig )
configKind := config .engineKind
configEngine := config .newEngine
if configKind == engineKindAuto {
if platform .CompilerSupports (config .enabledFeatures ) {
configKind = engineKindCompiler
} else {
configKind = engineKindInterpreter
}
}
if configEngine == nil {
if configKind == engineKindCompiler {
configEngine = wazevo .NewEngine
} else {
configEngine = interpreter .NewEngine
}
}
var engine wasm .Engine
var cacheImpl *cache
if c := config .cache ; c != nil {
cacheImpl = c .(*cache )
engine = cacheImpl .initEngine (configKind , configEngine , ctx , config .enabledFeatures )
} else {
engine = configEngine (ctx , config .enabledFeatures , nil )
}
store := wasm .NewStore (config .enabledFeatures , engine )
return &runtime {
cache : cacheImpl ,
store : store ,
enabledFeatures : config .enabledFeatures ,
memoryLimitPages : config .memoryLimitPages ,
memoryCapacityFromMax : config .memoryCapacityFromMax ,
dwarfDisabled : config .dwarfDisabled ,
storeCustomSections : config .storeCustomSections ,
ensureTermination : config .ensureTermination ,
}
}
type runtime struct {
store *wasm .Store
cache *cache
enabledFeatures api .CoreFeatures
memoryLimitPages uint32
memoryCapacityFromMax bool
dwarfDisabled bool
storeCustomSections bool
closed atomic .Uint64
ensureTermination bool
}
func (r *runtime ) Module (moduleName string ) api .Module {
if len (moduleName ) == 0 {
return nil
}
m := r .store .Module (moduleName )
if m == nil {
return nil
} else if m .Source .IsHostModule {
return hostModuleInstance {m }
}
return m
}
func (r *runtime ) CompileModule (ctx context .Context , binary []byte ) (CompiledModule , error ) {
if err := r .failIfClosed (); err != nil {
return nil , err
}
internal , err := binaryformat .DecodeModule (binary , r .enabledFeatures ,
r .memoryLimitPages , r .memoryCapacityFromMax , !r .dwarfDisabled , r .storeCustomSections )
if err != nil {
return nil , err
} else if err = internal .Validate (r .enabledFeatures ); err != nil {
return nil , err
}
internal .BuildMemoryDefinitions ()
c := &compiledModule {module : internal , compiledEngine : r .store .Engine }
typeIDs , err := r .store .GetFunctionTypeIDs (internal .TypeSection )
if err != nil {
return nil , err
}
c .typeIDs = typeIDs
listeners , err := buildFunctionListeners (ctx , internal )
if err != nil {
return nil , err
}
internal .AssignModuleID (binary , listeners , r .ensureTermination )
if err = r .store .Engine .CompileModule (ctx , internal , listeners , r .ensureTermination ); err != nil {
return nil , err
}
return c , nil
}
func buildFunctionListeners(ctx context .Context , internal *wasm .Module ) ([]experimentalapi .FunctionListener , error ) {
fnlf := ctx .Value (expctxkeys .FunctionListenerFactoryKey {})
if fnlf == nil {
return nil , nil
}
factory := fnlf .(experimentalapi .FunctionListenerFactory )
importCount := internal .ImportFunctionCount
listeners := make ([]experimentalapi .FunctionListener , len (internal .FunctionSection ))
for i := 0 ; i < len (listeners ); i ++ {
listeners [i ] = factory .NewFunctionListener (internal .FunctionDefinition (uint32 (i ) + importCount ))
}
return listeners , nil
}
func (r *runtime ) failIfClosed () error {
if closed := r .closed .Load (); closed != 0 {
return fmt .Errorf ("runtime closed with exit_code(%d)" , uint32 (closed >>32 ))
}
return nil
}
func (r *runtime ) Instantiate (ctx context .Context , binary []byte ) (api .Module , error ) {
return r .InstantiateWithConfig (ctx , binary , NewModuleConfig ())
}
func (r *runtime ) InstantiateWithConfig (ctx context .Context , binary []byte , config ModuleConfig ) (api .Module , error ) {
if compiled , err := r .CompileModule (ctx , binary ); err != nil {
return nil , err
} else {
compiled .(*compiledModule ).closeWithModule = true
return r .InstantiateModule (ctx , compiled , config )
}
}
func (r *runtime ) InstantiateModule (
ctx context .Context ,
compiled CompiledModule ,
mConfig ModuleConfig ,
) (mod api .Module , err error ) {
if err = r .failIfClosed (); err != nil {
return nil , err
}
code := compiled .(*compiledModule )
config := mConfig .(*moduleConfig )
if !code .module .IsHostModule {
if sockConfig , ok := ctx .Value (internalsock .ConfigKey {}).(*internalsock .Config ); ok {
config .sockConfig = sockConfig
}
}
var sysCtx *internalsys .Context
if sysCtx , err = config .toSysContext (); err != nil {
return nil , err
}
name := config .name
if !config .nameSet && code .module .NameSection != nil && code .module .NameSection .ModuleName != "" {
name = code .module .NameSection .ModuleName
}
mod , err = r .store .Instantiate (ctx , code .module , name , sysCtx , code .typeIDs )
if err != nil {
if code .closeWithModule {
_ = code .Close (ctx )
}
return nil , err
}
if closeNotifier , ok := ctx .Value (expctxkeys .CloseNotifierKey {}).(experimentalapi .CloseNotifier ); ok {
mod .(*wasm .ModuleInstance ).CloseNotifier = closeNotifier
}
if code .closeWithModule {
mod .(*wasm .ModuleInstance ).CodeCloser = code
}
for _ , fn := range config .startFunctions {
start := mod .ExportedFunction (fn )
if start == nil {
continue
}
if _, err = start .Call (ctx ); err != nil {
_ = mod .Close (ctx )
if se , ok := err .(*sys .ExitError ); ok {
if se .ExitCode () == 0 {
err = nil
}
return
}
err = fmt .Errorf ("module[%s] function[%s] failed: %w" , name , fn , err )
return
}
}
return
}
func (r *runtime ) Close (ctx context .Context ) error {
return r .CloseWithExitCode (ctx , 0 )
}
func (r *runtime ) CloseWithExitCode (ctx context .Context , exitCode uint32 ) error {
closed := uint64 (1 ) + uint64 (exitCode )<<32
if !r .closed .CompareAndSwap (0 , closed ) {
return nil
}
err := r .store .CloseWithExitCode (ctx , exitCode )
if r .cache == nil {
if errCloseEngine := r .store .Engine .Close (); errCloseEngine != nil {
return errCloseEngine
}
}
return err
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .