1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
package wasm
import (
"context"
"errors"
"io"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/sys"
)
type Args struct {
// 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
}
type Instantiator struct {
// Module ...
Module string
// Runtime ...
Runtime func(context.Context) wazero.Runtime
// Config ...
Config func() wazero.ModuleConfig
// Source ...
Source []byte
}
func (inst *Instantiator) New(ctx context.Context) (*Instance, error) {
switch {
case inst.Module == "":
panic("missing module name")
case inst.Runtime == nil:
panic("missing runtime instantiator")
case inst.Config == nil:
panic("missing module configuration")
case len(inst.Source) == 0:
panic("missing module source")
}
// Create new host runtime.
rt := inst.Runtime(ctx)
// Compile guest module from WebAssembly source.
mod, err := rt.CompileModule(ctx, inst.Source)
if err != nil {
return nil, err
}
return &Instance{
inst: inst,
wzrt: rt,
cmod: mod,
}, nil
}
// Instance ...
//
// NOTE: Instance is NOT concurrency
// safe. One at a time please!!
type Instance struct {
inst *Instantiator
wzrt wazero.Runtime
cmod wazero.CompiledModule
}
func (inst *Instance) Run(ctx context.Context, args Args) (uint32, error) {
if inst.inst == nil {
panic("not initialized")
}
// Check instance open.
if inst.IsClosed() {
return 0, errors.New("instance closed")
}
// Prefix binary name as argv0 to args.
cargs := make([]string, len(args.Args)+1)
copy(cargs[1:], args.Args)
cargs[0] = inst.inst.Module
// Create base module config.
modcfg := inst.inst.Config()
modcfg = modcfg.WithName(inst.inst.Module)
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)
}
// Instantiate the module from precompiled wasm module data.
mod, err := inst.wzrt.InstantiateModule(ctx, inst.cmod, modcfg)
switch err := err.(type) {
case nil:
return 0, mod.Close(ctx)
case *sys.ExitError:
return err.ExitCode(), nil
default:
return 0, err
}
}
func (inst *Instance) IsClosed() bool {
return (inst.wzrt == nil || inst.cmod == nil)
}
func (inst *Instance) Close(ctx context.Context) error {
if inst.IsClosed() {
return nil
}
err1 := inst.cmod.Close(ctx)
err2 := inst.wzrt.Close(ctx)
return errors.Join(err1, err2)
}
|