Currently, we have custom code in wapc-go because our library forces a failure when a module that uses WASI doesn't define a "_start" function. Using the same pragmatism that resulted in us not enforcing the WASI table, this makes the "_start" function optional. This doesn't add a flag as the spec is not a proper version anyway (snapshot-01), so there's no need to further complicate configuration. If a "_start" function exists, we enforce it is of the proper signature and succeeds. Otherwise, we allow it to be absent. Signed-off-by: Adrian Cole <adrian@tetrate.io> Co-authored-by: Takeshi Yoneda <takeshi@tetrate.io>
94 lines
2.6 KiB
Go
94 lines
2.6 KiB
Go
package wazero
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
_ "embed"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/tetratelabs/wazero/wasm"
|
|
)
|
|
|
|
// TestStartWASICommand_DoesntEnforce_Start ensures wapc-go work when modules import WASI, but don't export "_start".
|
|
func TestStartWASICommand_DoesntEnforce_Start(t *testing.T) {
|
|
r := NewRuntime()
|
|
|
|
wasi, err := r.InstantiateModule(WASISnapshotPreview1())
|
|
require.NoError(t, err)
|
|
defer wasi.Close()
|
|
|
|
// Start the module as a WASI command. This will fail if the context wasn't as intended.
|
|
mod, err := StartWASICommandFromSource(r, []byte(`(module $wasi_test.go
|
|
(memory 1)
|
|
(export "memory" (memory 0))
|
|
)`))
|
|
require.NoError(t, err)
|
|
require.NoError(t, mod.Close())
|
|
}
|
|
|
|
func TestStartWASICommand_UsesRuntimeContext(t *testing.T) {
|
|
type key string
|
|
config := NewRuntimeConfig().WithContext(context.WithValue(context.Background(), key("wa"), "zero"))
|
|
r := NewRuntimeWithConfig(config)
|
|
|
|
// Define a function that will be re-exported as the WASI function: _start
|
|
var calledStart bool
|
|
start := func(ctx wasm.Module) {
|
|
calledStart = true
|
|
require.Equal(t, config.ctx, ctx.Context())
|
|
}
|
|
|
|
host, err := r.NewModuleBuilder("").ExportFunction("start", start).Instantiate()
|
|
require.NoError(t, err)
|
|
defer host.Close()
|
|
|
|
wasi, err := r.InstantiateModule(WASISnapshotPreview1())
|
|
require.NoError(t, err)
|
|
defer wasi.Close()
|
|
|
|
// Start the module as a WASI command. This will fail if the context wasn't as intended.
|
|
mod, err := StartWASICommandFromSource(r, []byte(`(module $wasi_test.go
|
|
(import "" "start" (func $start))
|
|
(memory 1)
|
|
(export "_start" (func $start))
|
|
(export "memory" (memory 0))
|
|
)`))
|
|
require.NoError(t, err)
|
|
defer mod.Close()
|
|
|
|
require.True(t, calledStart)
|
|
}
|
|
|
|
// wasiArg was compiled from examples/testdata/wasi_arg.wat
|
|
//go:embed examples/testdata/wasi_arg.wasm
|
|
var wasiArg []byte
|
|
|
|
func TestStartWASICommandWithConfig(t *testing.T) {
|
|
r := NewRuntime()
|
|
|
|
stdout := bytes.NewBuffer(nil)
|
|
|
|
// Configure WASI to write stdout to a buffer, so that we can verify it later.
|
|
sys := NewSysConfig().WithStdout(stdout)
|
|
wasi, err := r.InstantiateModule(WASISnapshotPreview1())
|
|
require.NoError(t, err)
|
|
defer wasi.Close()
|
|
|
|
m, err := r.CompileModule(wasiArg)
|
|
require.NoError(t, err)
|
|
|
|
// Re-use the same module many times.
|
|
for _, tc := range []string{"a", "b", "c"} {
|
|
mod, err := StartWASICommandWithConfig(r, m.WithName(tc), sys.WithArgs(tc))
|
|
require.NoError(t, err)
|
|
|
|
// Ensure the scoped configuration applied. As the args are null-terminated, we append zero (NUL).
|
|
require.Equal(t, append([]byte(tc), 0), stdout.Bytes())
|
|
|
|
stdout.Reset()
|
|
require.NoError(t, mod.Close())
|
|
}
|
|
}
|