2024-10-06 23:53:03 +03:00
|
|
|
package wasm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"io"
|
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
"github.com/tetratelabs/wazero"
|
|
|
|
"github.com/tetratelabs/wazero/sys"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Args encompasses a common set of
|
|
|
|
// configuration options often passed to
|
|
|
|
// wazero.Runtime on module instantiation.
|
|
|
|
type Args struct {
|
|
|
|
|
2024-11-06 16:38:13 +03:00
|
|
|
// Program name, depending on the
|
|
|
|
// module being run this may or may
|
|
|
|
// not be necessary.
|
|
|
|
Name string
|
|
|
|
|
2024-10-06 23:53:03 +03:00
|
|
|
// Optional further module configuration function.
|
|
|
|
// (e.g. to mount filesystem dir, set env vars, etc).
|
|
|
|
Config func(wazero.ModuleConfig) wazero.ModuleConfig
|
|
|
|
|
|
|
|
// Standard FDs.
|
|
|
|
Stdin io.Reader
|
|
|
|
Stdout io.Writer
|
|
|
|
Stderr io.Writer
|
|
|
|
|
|
|
|
// CLI args.
|
|
|
|
Args []string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run will run given compiled WebAssembly module
|
|
|
|
// within the given runtime, with given arguments.
|
|
|
|
// Returns the exit code, or error.
|
|
|
|
func Run(
|
|
|
|
ctx context.Context,
|
|
|
|
runtime wazero.Runtime,
|
|
|
|
module wazero.CompiledModule,
|
|
|
|
args Args,
|
|
|
|
) (rc uint32, err error) {
|
|
|
|
|
|
|
|
// Prefix arguments with module name.
|
|
|
|
cargs := make([]string, len(args.Args)+1)
|
2024-11-06 16:38:13 +03:00
|
|
|
cargs[0] = args.Name
|
2024-10-06 23:53:03 +03:00
|
|
|
copy(cargs[1:], args.Args)
|
|
|
|
|
|
|
|
// Prepare new module configuration.
|
|
|
|
modcfg := wazero.NewModuleConfig()
|
|
|
|
modcfg = modcfg.WithArgs(cargs...)
|
|
|
|
modcfg = modcfg.WithStdin(args.Stdin)
|
|
|
|
modcfg = modcfg.WithStdout(args.Stdout)
|
|
|
|
modcfg = modcfg.WithStderr(args.Stderr)
|
|
|
|
|
|
|
|
if args.Config != nil {
|
|
|
|
// Pass through config fn.
|
|
|
|
modcfg = args.Config(modcfg)
|
|
|
|
}
|
|
|
|
|
2024-10-28 13:55:48 +03:00
|
|
|
// Enable setjmp longjmp.
|
|
|
|
ctx = withSetjmpLongjmp(ctx)
|
|
|
|
|
2024-10-06 23:53:03 +03:00
|
|
|
// Instantiate the module from precompiled wasm module data.
|
|
|
|
mod, err := runtime.InstantiateModule(ctx, module, modcfg)
|
|
|
|
|
|
|
|
if !isNil(mod) {
|
|
|
|
// Ensure closed.
|
|
|
|
_ = mod.Close(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try extract exit code.
|
|
|
|
switch err := err.(type) {
|
|
|
|
case *sys.ExitError:
|
|
|
|
return err.ExitCode(), nil
|
|
|
|
default:
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// isNil will safely check if 'v' is nil without
|
|
|
|
// dealing with weird Go interface nil bullshit.
|
|
|
|
func isNil(i interface{}) bool {
|
|
|
|
type eface struct{ Type, Data unsafe.Pointer }
|
|
|
|
return (*(*eface)(unsafe.Pointer(&i))).Data == nil
|
|
|
|
}
|