diff --git a/extism.go b/extism.go index 5c24499..def51fa 100644 --- a/extism.go +++ b/extism.go @@ -37,10 +37,9 @@ func RuntimeVersion() string { // Runtime represents the Extism plugin's runtime environment, including the underlying Wazero runtime and modules. type Runtime struct { - Wazero wazero.Runtime - Extism api.Module - Env api.Module - hasWasi bool + Wazero wazero.Runtime + Extism api.Module + Env api.Module } // PluginInstanceConfig contains configuration options for the Extism plugin. @@ -112,13 +111,12 @@ func (l LogLevel) String() string { // Plugin is used to call WASM functions type Plugin struct { - close []func(ctx context.Context) error - extism api.Module - - module api.Module - Timeout time.Duration - Config map[string]string - // NOTE: maybe we can have some nice methods for getting/setting vars + close []func(ctx context.Context) error + extism api.Module + mainModule api.Module + modules map[string]api.Module + Timeout time.Duration + Config map[string]string Var map[string][]byte AllowedHosts []string AllowedPaths map[string]string @@ -138,7 +136,7 @@ func logStd(level LogLevel, message string) { } func (p *Plugin) Module() *Module { - return &Module{inner: p.module} + return &Module{inner: p.mainModule} } // SetLogger sets a custom logging callback @@ -443,7 +441,7 @@ func (p *Plugin) GetErrorWithContext(ctx context.Context) string { // FunctionExists returns true when the named function is present in the plugin's main Module func (p *Plugin) FunctionExists(name string) bool { - return p.module.ExportedFunction(name) != nil + return p.mainModule.ExportedFunction(name) != nil } // Call a function by name with the given input, returning the output @@ -469,7 +467,7 @@ func (p *Plugin) CallWithContext(ctx context.Context, name string, data []byte) ctx = context.WithValue(ctx, InputOffsetKey("inputOffset"), intputOffset) - var f = p.module.ExportedFunction(name) + var f = p.mainModule.ExportedFunction(name) if f == nil { return 1, []byte{}, fmt.Errorf("unknown function: %s", name) @@ -477,7 +475,7 @@ func (p *Plugin) CallWithContext(ctx context.Context, name string, data []byte) return 1, []byte{}, fmt.Errorf("function %s has %v results, expected 0 or 1", name, n) } - var isStart = name == "_start" + var isStart = name == "_start" || name == "_initialize" if p.guestRuntime.init != nil && !isStart && !p.guestRuntime.initialized { err := p.guestRuntime.init(ctx) if err != nil { @@ -501,7 +499,7 @@ func (p *Plugin) CallWithContext(ctx context.Context, name string, data []byte) if exitCode == 0 { // It's possible for the function to return 0 as an error code, even // if the module is closed. - if p.module.IsClosed() { + if p.mainModule.IsClosed() { return 0, nil, fmt.Errorf("module is closed") } err = nil diff --git a/extism_test.go b/extism_test.go index 38b3584..5c8f109 100644 --- a/extism_test.go +++ b/extism_test.go @@ -5,6 +5,13 @@ import ( "context" "encoding/json" "fmt" + "log" + "os" + "strings" + "sync" + "testing" + "time" + observe "github.com/dylibso/observe-sdk/go" "github.com/dylibso/observe-sdk/go/adapter/stdout" "github.com/stretchr/testify/assert" @@ -13,12 +20,6 @@ import ( "github.com/tetratelabs/wazero/experimental" "github.com/tetratelabs/wazero/experimental/logging" "github.com/tetratelabs/wazero/sys" - "log" - "os" - "strings" - "sync" - "testing" - "time" ) func TestWasmUrl(t *testing.T) { @@ -1038,6 +1039,170 @@ func TestEnableExperimentalFeature(t *testing.T) { } } +func TestModuleLinking(t *testing.T) { + manifest := Manifest{ + Wasm: []Wasm{ + WasmFile{ + Path: "wasm/lib.wasm", + Name: "lib", + }, + WasmFile{ + Path: "wasm/main.wasm", + Name: "main", + }, + }, + } + + if plugin, ok := pluginInstance(t, manifest); ok { + defer plugin.Close(context.Background()) + + exit, output, err := plugin.Call("run_test", []byte("benjamin")) + + if assertCall(t, err, exit) { + expected := "Hello, BENJAMIN" + + actual := string(output) + + assert.Equal(t, expected, actual) + } + } +} + +func TestModuleLinkingMultipleInstances(t *testing.T) { + manifest := Manifest{ + Wasm: []Wasm{ + WasmFile{ + Path: "wasm/lib.wasm", + Name: "lib", + }, + WasmFile{ + Path: "wasm/main.wasm", + Name: "main", + }, + }, + } + + ctx := context.Background() + config := wasiPluginConfig() + + compiledPlugin, err := NewCompiledPlugin(ctx, manifest, PluginConfig{ + EnableWasi: true, + }, []HostFunction{}) + + if err != nil { + t.Fatalf("Could not create plugin: %v", err) + } + + for i := 0; i < 3; i++ { + plugin, err := compiledPlugin.Instance(ctx, config) + if err != nil { + t.Fatalf("Could not create plugin instance: %v", err) + } + // purposefully not closing the plugin instance + + for j := 0; j < 3; j++ { + + exit, output, err := plugin.Call("run_test", []byte("benjamin")) + + if assertCall(t, err, exit) { + expected := "Hello, BENJAMIN" + + actual := string(output) + + assert.Equal(t, expected, actual) + } + } + } +} + +func TestCompiledModuleMultipleInstances(t *testing.T) { + manifest := Manifest{ + Wasm: []Wasm{ + WasmFile{ + Path: "wasm/count_vowels.wasm", + Name: "main", + }, + }, + } + + ctx := context.Background() + config := wasiPluginConfig() + + compiledPlugin, err := NewCompiledPlugin(ctx, manifest, PluginConfig{ + EnableWasi: true, + }, []HostFunction{}) + + if err != nil { + t.Fatalf("Could not create plugin: %v", err) + } + + var wg sync.WaitGroup + numInstances := 300 + + // Create and test instances in parallel + for i := 0; i < numInstances; i++ { + wg.Add(1) + go func(instanceNum int) { + defer wg.Done() + + plugin, err := compiledPlugin.Instance(ctx, config) + if err != nil { + t.Errorf("Could not create plugin instance %d: %v", instanceNum, err) + return + } + // purposefully not closing the plugin instance + + // Sequential calls for this instance + for j := 0; j < 3; j++ { + exit, _, err := plugin.Call("count_vowels", []byte("benjamin")) + if err != nil { + t.Errorf("Instance %d, call %d failed: %v", instanceNum, j, err) + return + } + if exit != 0 { + t.Errorf("Instance %d, call %d returned non-zero exit code: %d", instanceNum, j, exit) + } + } + }(i) + } + wg.Wait() +} + +func TestMultipleCallsOutputParallel(t *testing.T) { + manifest := manifest("count_vowels.wasm") + numInstances := 300 + + var wg sync.WaitGroup + + // Create and test instances in parallel + for i := 0; i < numInstances; i++ { + wg.Add(1) + go func(instanceNum int) { + defer wg.Done() + + if plugin, ok := pluginInstance(t, manifest); ok { + defer plugin.Close(context.Background()) + + // Sequential calls for this instance + exit, output1, err := plugin.Call("count_vowels", []byte("aaa")) + if !assertCall(t, err, exit) { + return + } + + exit, output2, err := plugin.Call("count_vowels", []byte("bbba")) + if !assertCall(t, err, exit) { + return + } + + assert.Equal(t, `{"count":3,"total":3,"vowels":"aeiouAEIOU"}`, string(output1)) + assert.Equal(t, `{"count":1,"total":4,"vowels":"aeiouAEIOU"}`, string(output2)) + } + }(i) + } + + wg.Wait() +} + func BenchmarkInitialize(b *testing.B) { ctx := context.Background() cache := wazero.NewCompilationCache() diff --git a/plugin.go b/plugin.go index a14e101..605c845 100644 --- a/plugin.go +++ b/plugin.go @@ -1,3 +1,4 @@ +// new package extism import ( @@ -5,7 +6,6 @@ import ( "errors" "fmt" "os" - "strconv" "strings" "sync/atomic" "time" @@ -21,6 +21,7 @@ type CompiledPlugin struct { main wazero.CompiledModule extism wazero.CompiledModule env api.Module + modules map[string]wazero.CompiledModule // when a module (main) is instantiated, it may have a module name that's added // to the data section of the wasm. If this is the case, we won't be able to @@ -87,6 +88,24 @@ func NewPlugin( return p, nil } +func calculateMaxHttp(manifest Manifest) int64 { + // Default is 50MB + maxHttp := int64(1024 * 1024 * 50) + if manifest.Memory != nil && manifest.Memory.MaxHttpResponseBytes >= 0 { + maxHttp = manifest.Memory.MaxHttpResponseBytes + } + return maxHttp +} + +func calculateMaxVar(manifest Manifest) int64 { + // Default is 1MB + maxVar := int64(1024 * 1024) + if manifest.Memory != nil && manifest.Memory.MaxVarBytes >= 0 { + maxVar = manifest.Memory.MaxVarBytes + } + return maxVar +} + // NewCompiledPlugin creates a compiled plugin that is ready to be instantiated. // You can instantiate the plugin multiple times using the CompiledPlugin.Instance // method and run those instances concurrently. @@ -123,6 +142,9 @@ func NewCompiledPlugin( observeAdapter: config.ObserveAdapter, observeOptions: config.ObserveOptions, enableHttpResponseHeaders: config.EnableHttpResponseHeaders, + modules: make(map[string]wazero.CompiledModule), + maxHttp: calculateMaxHttp(manifest), + maxVar: calculateMaxVar(manifest), } if config.EnableWasi { @@ -161,19 +183,18 @@ func NewCompiledPlugin( // - If there is only one module in the manifest then that is the main module by default // - Otherwise the last module listed is the main module - modules := map[string]wazero.CompiledModule{} + foundMain := false for i, wasm := range manifest.Wasm { data, err := wasm.ToWasmData(ctx) if err != nil { return nil, err } - _, mainExists := modules["main"] - if data.Name == "" || i == len(manifest.Wasm)-1 && !mainExists { + if (data.Name == "" || i == len(manifest.Wasm)-1) && !foundMain { data.Name = "main" } - _, okm := modules[data.Name] + _, okm := p.modules[data.Name] if data.Name == "extism:host/env" || okm { return nil, fmt.Errorf("module name collision: '%s'", data.Name) @@ -190,14 +211,25 @@ func NewCompiledPlugin( p.wasmBytes = data.Data } - m, err := p.runtime.CompileModule(ctx, data.Data) + compiledModule, err := p.runtime.CompileModule(ctx, data.Data) if err != nil { return nil, err } + if data.Name == "main" { - p.main = m + if foundMain { + return nil, errors.New("can't have more than one main module") + } + p.main = compiledModule + foundMain = true } else { - modules[data.Name] = m + // Store compiled module for instantiation + p.modules[data.Name] = compiledModule + // Create wrapper with original name that will forward calls to the actual module instance. See createModuleWrapper for more details. + _, err = createModuleWrapper(ctx, p.runtime, data.Name, compiledModule) + if err != nil { + return nil, fmt.Errorf("failed to create wrapper for %s: %w", data.Name, err) + } } } @@ -212,11 +244,72 @@ func NewCompiledPlugin( return &p, nil } +// createModuleWrapper creates a host module that acts as a proxy for module instances. +// In Wazero, modules with the same name cannot be instantiated multiple times in the same runtime. +// However, we need each Plugin instance to have its own copy of each module for isolation. To solve this, we: +// 1. Create a host module wrapper that keeps the original module name (needed for imports to work) +// 2. Instantiate actual module copies with unique names for each Plugin +// 3. The wrapper forwards function calls to the correct module instance for each Plugin +func createModuleWrapper(ctx context.Context, rt wazero.Runtime, name string, compiled wazero.CompiledModule) (api.Module, error) { + builder := rt.NewHostModuleBuilder(name) + + // Create proxy functions for each exported function from the original module. + // These proxies will forward calls to the appropriate module instance. + for _, export := range compiled.ExportedFunctions() { + exportName := export.Name() + + // Skip wrapping the _start function since it's automatically called by wazero during instantiation. + // The wrapper functions require a Plugin instance in the context to work, but during wrapper + // instantiation there is no Plugin instance yet. + if exportName == "_start" { + continue + } + + // Create a proxy function that: + // 1. Gets the calling Plugin instance from context + // 2. Looks up that Plugin's copy of this module + // 3. Forwards the call to the actual function + wrapper := func(callCtx context.Context, mod api.Module, stack []uint64) { + // Get the Plugin instance that's making this call + plugin, ok := callCtx.Value(PluginCtxKey("plugin")).(*Plugin) + if !ok { + panic("Invalid context, `plugin` key not found") + } + + // Get this Plugin's instance of the module + actualModule, ok := plugin.modules[name] + if !ok { + panic(fmt.Sprintf("module %s not found in plugin", name)) + } + + // Forward the call to the actual module instance + fn := actualModule.ExportedFunction(exportName) + if fn == nil { + panic(fmt.Sprintf("function %s not found in module %s", exportName, name)) + } + + err := fn.CallWithStack(callCtx, stack) + if err != nil { + panic(err) + } + } + + // Export the proxy function with the same name and signature as the original + builder.NewFunctionBuilder(). + WithGoModuleFunction(api.GoModuleFunc(wrapper), export.ParamTypes(), export.ResultTypes()). + Export(exportName) + } + + return builder.Instantiate(ctx) +} + func (p *CompiledPlugin) Close(ctx context.Context) error { return p.runtime.Close(ctx) } func (p *CompiledPlugin) Instance(ctx context.Context, config PluginInstanceConfig) (*Plugin, error) { + instanceNum := p.instanceCount.Add(1) + var closers []func(ctx context.Context) error moduleConfig := config.ModuleConfig @@ -224,8 +317,6 @@ func (p *CompiledPlugin) Instance(ctx context.Context, config PluginInstanceConf moduleConfig = wazero.NewModuleConfig() } - moduleConfig = moduleConfig.WithName(strconv.Itoa(int(p.instanceCount.Add(1)))) - // NOTE: we don't want wazero to call the start function, we will initialize // the guest runtime manually. // See: https://github.com/extism/go-sdk/pull/1#issuecomment-1650527495 @@ -258,8 +349,6 @@ func (p *CompiledPlugin) Instance(ctx context.Context, config PluginInstanceConf if err != nil { return nil, fmt.Errorf("failed to initialize Observe Adapter: %v", err) } - - trace.Finish() } // Compile and instantiate the extism runtime. This runtime is stateful and needs to be @@ -271,33 +360,47 @@ func (p *CompiledPlugin) Instance(ctx context.Context, config PluginInstanceConf if err != nil { return nil, fmt.Errorf("instantiating extism module: %w", err) } + closers = append(closers, extism.Close) - main, err := p.runtime.InstantiateModule(ctx, p.main, moduleConfig) - if err != nil { - return nil, fmt.Errorf("instantiating module: %w", err) + // Instantiate all non-main modules first + instancedModules := make(map[string]api.Module) + for name, compiledModule := range p.modules { + uniqueName := fmt.Sprintf("%s_%d", name, instanceNum) + instance, err := p.runtime.InstantiateModule(ctx, compiledModule, moduleConfig.WithName(uniqueName)) + if err != nil { + for _, closer := range closers { + closer(ctx) + } + return nil, fmt.Errorf("instantiating module %s: %w", name, err) + } + instancedModules[name] = instance + closers = append(closers, instance.Close) } - closers = append(closers, main.Close) - p.maxHttp = int64(1024 * 1024 * 50) - if p.manifest.Memory != nil && p.manifest.Memory.MaxHttpResponseBytes >= 0 { - p.maxHttp = p.manifest.Memory.MaxHttpResponseBytes - } + mainModuleName := fmt.Sprintf("main_%d", instanceNum) + main, err := p.runtime.InstantiateModule(ctx, p.main, moduleConfig.WithName(mainModuleName)) + if err != nil { + for _, closer := range closers { + closer(ctx) + } - p.maxVar = int64(1024 * 1024) - if p.manifest.Memory != nil && p.manifest.Memory.MaxVarBytes >= 0 { - p.maxVar = p.manifest.Memory.MaxVarBytes + return nil, fmt.Errorf("instantiating module: %w", err) } + closers = append(closers, main.Close) + var headers map[string]string = nil if p.enableHttpResponseHeaders { headers = map[string]string{} } + instance := &Plugin{ close: closers, extism: extism, hasWasi: p.hasWasi, - module: main, + mainModule: main, + modules: instancedModules, Timeout: time.Duration(p.manifest.Timeout) * time.Millisecond, Config: p.manifest.Config, Var: make(map[string][]byte), @@ -312,6 +415,7 @@ func (p *CompiledPlugin) Instance(ctx context.Context, config PluginInstanceConf log: logStd, traceCtx: trace, } - instance.guestRuntime = detectGuestRuntime(ctx, instance) + instance.guestRuntime = detectGuestRuntime(instance) + return instance, nil } diff --git a/plugins/lib/Makefile b/plugins/lib/Makefile new file mode 100644 index 0000000..19ed3fb --- /dev/null +++ b/plugins/lib/Makefile @@ -0,0 +1,2 @@ +build: + tinygo build -target wasi -o ../../wasm/lib.wasm main.go \ No newline at end of file diff --git a/plugins/lib/go.mod b/plugins/lib/go.mod new file mode 100644 index 0000000..4f6f590 --- /dev/null +++ b/plugins/lib/go.mod @@ -0,0 +1,5 @@ +module github.com/extism/extism-sdk-plugins-lib + +go 1.22 + +require github.com/extism/go-pdk v1.0.2 diff --git a/plugins/lib/go.sum b/plugins/lib/go.sum new file mode 100644 index 0000000..8897b44 --- /dev/null +++ b/plugins/lib/go.sum @@ -0,0 +1,2 @@ +github.com/extism/go-pdk v1.0.2 h1:UB7oTW3tw2zoMlsUdBEDAAbhQg9OudzgNeyCwQYZ730= +github.com/extism/go-pdk v1.0.2/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= diff --git a/plugins/lib/main.go b/plugins/lib/main.go new file mode 100644 index 0000000..023f085 --- /dev/null +++ b/plugins/lib/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "strings" + + pdk "github.com/extism/go-pdk" +) + +//go:export capitalize +func Capitalize(ptr uint64) uint64 { + mem := pdk.FindMemory(ptr) + bytes := mem.ReadBytes() + capitalized := strings.ToUpper(string(bytes)) + out := pdk.AllocateString(capitalized) + return out.Offset() +} + +func main() {} diff --git a/plugins/main/Makefile b/plugins/main/Makefile new file mode 100644 index 0000000..87dae71 --- /dev/null +++ b/plugins/main/Makefile @@ -0,0 +1,2 @@ +build: + tinygo build -target wasi -o ../../wasm/main.wasm main.go \ No newline at end of file diff --git a/plugins/main/go.mod b/plugins/main/go.mod new file mode 100644 index 0000000..f4eeda5 --- /dev/null +++ b/plugins/main/go.mod @@ -0,0 +1,5 @@ +module github.com/extism/extism-sdk-plugins-main + +go 1.22 + +require github.com/extism/go-pdk v1.0.2 diff --git a/plugins/main/go.sum b/plugins/main/go.sum new file mode 100644 index 0000000..8897b44 --- /dev/null +++ b/plugins/main/go.sum @@ -0,0 +1,2 @@ +github.com/extism/go-pdk v1.0.2 h1:UB7oTW3tw2zoMlsUdBEDAAbhQg9OudzgNeyCwQYZ730= +github.com/extism/go-pdk v1.0.2/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= diff --git a/plugins/main/main.go b/plugins/main/main.go new file mode 100644 index 0000000..8d6bc0d --- /dev/null +++ b/plugins/main/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "github.com/extism/go-pdk" +) + +//go:wasm-module lib +//export capitalize +func Capitalize(offset uint64) uint64 + +//go:export run_test +func run_test() int32 { + name := pdk.InputString() + + ptr := pdk.AllocateString(name) + capitalizedPtr := Capitalize(ptr.Offset()) + capitalizedMem := pdk.FindMemory(capitalizedPtr) + capitalized := string(capitalizedMem.ReadBytes()) + + pdk.OutputString("Hello, " + capitalized) + return 0 +} + +func main() {} diff --git a/runtime.go b/runtime.go index 61393b0..5aa4230 100644 --- a/runtime.go +++ b/runtime.go @@ -2,6 +2,7 @@ package extism import ( "context" + "github.com/tetratelabs/wazero/api" ) @@ -16,33 +17,78 @@ const ( ) type guestRuntime struct { + mainRuntime moduleRuntime + runtimes map[string]moduleRuntime + init func(ctx context.Context) error + initialized bool +} + +type moduleRuntime struct { runtimeType runtimeType init func(ctx context.Context) error initialized bool } -func detectGuestRuntime(ctx context.Context, p *Plugin) guestRuntime { - runtime, ok := haskellRuntime(ctx, p, p.module) +// detectGuestRuntime detects the runtime of the main module and all other modules +// it returns a guest runtime with an initialization function specific that invokes +// the initialization function of all the modules, with the main module last. +func detectGuestRuntime(p *Plugin) guestRuntime { + r := guestRuntime{runtimes: make(map[string]moduleRuntime)} + + r.mainRuntime = detectModuleRuntime(p, p.mainModule) + for k, m := range p.modules { + r.runtimes[k] = detectModuleRuntime(p, m) + } + + r.init = func(ctx context.Context) error { + + for k, v := range r.runtimes { + p.Logf(LogLevelDebug, "Initializing runtime for module %v", k) + err := v.init(ctx) + if err != nil { + return err + } + v.initialized = true + } + + m := r.mainRuntime + p.Logf(LogLevelDebug, "Initializing runtime for main module") + err := m.init(ctx) + if err != nil { + return err + } + m.initialized = true + + return nil + } + + return r +} + +// detectModuleRuntime detects the specific runtime of a given module +// it returns a module runtime with an initialization function specific to that module +func detectModuleRuntime(p *Plugin, m api.Module) moduleRuntime { + runtime, ok := haskellRuntime(p, m) if ok { return runtime } - runtime, ok = wasiRuntime(ctx, p, p.module) + runtime, ok = wasiRuntime(p, m) if ok { return runtime } p.Log(LogLevelTrace, "No runtime detected") - return guestRuntime{runtimeType: None, init: func(_ context.Context) error { return nil }, initialized: true} + return moduleRuntime{runtimeType: None, init: func(_ context.Context) error { return nil }, initialized: true} } // Check for Haskell runtime initialization functions // Initialize Haskell runtime if `hs_init` and `hs_exit` are present, // by calling the `hs_init` export -func haskellRuntime(ctx context.Context, p *Plugin, m api.Module) (guestRuntime, bool) { +func haskellRuntime(p *Plugin, m api.Module) (moduleRuntime, bool) { initFunc := m.ExportedFunction("hs_init") if initFunc == nil { - return guestRuntime{}, false + return moduleRuntime{}, false } params := initFunc.Definition().ParamTypes() @@ -69,53 +115,53 @@ func haskellRuntime(ctx context.Context, p *Plugin, m api.Module) (guestRuntime, } p.Log(LogLevelTrace, "Haskell runtime detected") - return guestRuntime{runtimeType: Haskell, init: init}, true + return moduleRuntime{runtimeType: Haskell, init: init}, true } // Check for initialization functions defined by the WASI standard -func wasiRuntime(ctx context.Context, p *Plugin, m api.Module) (guestRuntime, bool) { +func wasiRuntime(p *Plugin, m api.Module) (moduleRuntime, bool) { if !p.hasWasi { - return guestRuntime{}, false + return moduleRuntime{}, false } // WASI supports two modules: Reactors and Commands // we prioritize Reactors over Commands // see: https://github.com/WebAssembly/WASI/blob/main/legacy/application-abi.md - if r, ok := reactorModule(ctx, m, p); ok { + if r, ok := reactorModule(m, p); ok { return r, ok } - return commandModule(ctx, m, p) + return commandModule(m, p) } // Check for `_initialize` this is used by WASI to initialize certain interfaces. -func reactorModule(ctx context.Context, m api.Module, p *Plugin) (guestRuntime, bool) { - init := findFunc(ctx, m, p, "_initialize") +func reactorModule(m api.Module, p *Plugin) (moduleRuntime, bool) { + init := findFunc(m, p, "_initialize") if init == nil { - return guestRuntime{}, false + return moduleRuntime{}, false } p.Logf(LogLevelTrace, "WASI runtime detected") p.Logf(LogLevelTrace, "Reactor module detected") - return guestRuntime{runtimeType: Wasi, init: init}, true + return moduleRuntime{runtimeType: Wasi, init: init}, true } // Check for `__wasm__call_ctors`, this is used by WASI to // initialize certain interfaces. -func commandModule(ctx context.Context, m api.Module, p *Plugin) (guestRuntime, bool) { - init := findFunc(ctx, m, p, "__wasm_call_ctors") +func commandModule(m api.Module, p *Plugin) (moduleRuntime, bool) { + init := findFunc(m, p, "__wasm_call_ctors") if init == nil { - return guestRuntime{}, false + return moduleRuntime{}, false } p.Logf(LogLevelTrace, "WASI runtime detected") p.Logf(LogLevelTrace, "Command module detected") - return guestRuntime{runtimeType: Wasi, init: init}, true + return moduleRuntime{runtimeType: Wasi, init: init}, true } -func findFunc(ctx context.Context, m api.Module, p *Plugin, name string) func(context.Context) error { +func findFunc(m api.Module, p *Plugin, name string) func(context.Context) error { initFunc := m.ExportedFunction(name) if initFunc == nil { return nil diff --git a/wasm/lib.wasm b/wasm/lib.wasm new file mode 100644 index 0000000..e98d1e4 Binary files /dev/null and b/wasm/lib.wasm differ diff --git a/wasm/main.wasm b/wasm/main.wasm new file mode 100644 index 0000000..258bed5 Binary files /dev/null and b/wasm/main.wasm differ