diff --git a/api/wasm.go b/api/wasm.go index 695856c4..3c31f3e9 100644 --- a/api/wasm.go +++ b/api/wasm.go @@ -125,7 +125,7 @@ func ValueTypeName(t ValueType) string { // Module return functions exported in a module, post-instantiation. // -// Notes +// # Notes // // - Closing the wazero.Runtime closes any Module it instantiated. // - This is an interface for decoupling, not third-party implementations. All implementations are in wazero. @@ -316,7 +316,7 @@ type MutableGlobal interface { // Memory allows restricted access to a module's memory. Notably, this does not allow growing. // -// Notes +// # Notes // // - All functions accept a context.Context, which when nil, default to context.Background. // - This is an interface for decoupling, not third-party implementations. All implementations are in wazero. @@ -334,11 +334,11 @@ type Memory interface { // The return val is the previous memory size in pages, or false if the // delta was ignored as it exceeds max memory. // - // Notes + // # Notes // - // * This is the same as the "memory.grow" instruction defined in the + // - This is the same as the "memory.grow" instruction defined in the // WebAssembly Core Specification, except returns false instead of -1. - // * When this returns true, any shared views via Read must be refreshed. + // - When this returns true, any shared views via Read must be refreshed. // // See MemorySizer Read and https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem Grow(ctx context.Context, deltaPages uint32) (previousPages uint32, ok bool) diff --git a/assemblyscript/assemblyscript.go b/assemblyscript/assemblyscript.go index 3cd522af..18a8c462 100644 --- a/assemblyscript/assemblyscript.go +++ b/assemblyscript/assemblyscript.go @@ -47,7 +47,7 @@ const ( // Instantiate instantiates the "env" module used by AssemblyScript into the // runtime default namespace. // -// Notes +// # Notes // // - Closing the wazero.Runtime has the same effect as closing the result. // - To add more functions to the "env" module, use FunctionExporter. diff --git a/assemblyscript/assemblyscript_test.go b/assemblyscript/assemblyscript_test.go index e3215c19..77c93fb3 100644 --- a/assemblyscript/assemblyscript_test.go +++ b/assemblyscript/assemblyscript_test.go @@ -14,6 +14,7 @@ import ( "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" . "github.com/tetratelabs/wazero/experimental" + "github.com/tetratelabs/wazero/experimental/logging" "github.com/tetratelabs/wazero/internal/testing/require" "github.com/tetratelabs/wazero/internal/u64" "github.com/tetratelabs/wazero/internal/wasm" @@ -398,7 +399,7 @@ func requireModule(t *testing.T, fns FunctionExporter, config wazero.ModuleConfi var log bytes.Buffer // Set context to one that has an experimental listener - ctx := context.WithValue(testCtx, FunctionListenerFactoryKey{}, NewLoggingListenerFactory(&log)) + ctx := context.WithValue(testCtx, FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&log)) r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfigInterpreter()) diff --git a/builder.go b/builder.go index 75bf18c2..2895a15e 100644 --- a/builder.go +++ b/builder.go @@ -36,7 +36,7 @@ import ( // // env2, _ := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("env.2")) // -// Notes +// # Notes // // - ModuleBuilder is mutable: each method returns the same instance for // chaining. @@ -50,11 +50,11 @@ type ModuleBuilder interface { // ExportFunction adds a function written in Go, which a WebAssembly module can import. // If a function is already exported with the same name, this overwrites it. // - // Parameters + // # Parameters // - // * exportName - The name to export. Ex "random_get" - // * goFunc - The `func` to export. - // * names - If present, the first is the api.FunctionDefinition name. + // - exportName - The name to export. Ex "random_get" + // - goFunc - The `func` to export. + // - names - If present, the first is the api.FunctionDefinition name. // If any follow, they must match the count of goFunc's parameters. // // Ex. @@ -116,19 +116,19 @@ type ModuleBuilder interface { // ExportMemory adds linear memory, which a WebAssembly module can import and become available via api.Memory. // If a memory is already exported with the same name, this overwrites it. // - // Parameters + // # Parameters // - // * name - the name to export. Ex "memory" for wasi_snapshot_preview1.ModuleSnapshotPreview1 - // * minPages - the possibly zero initial size in pages (65536 bytes per page). + // - name - the name to export. Ex "memory" for wasi_snapshot_preview1.ModuleSnapshotPreview1 + // - minPages - the possibly zero initial size in pages (65536 bytes per page). // // For example, the WebAssembly 1.0 Text Format below is the equivalent of this builder method: // // (memory (export "memory") 1) // builder.ExportMemory(1) // - // Notes + // # Notes // - // * This is allowed to grow to (4GiB) limited by api.MemorySizer. To bound it, use ExportMemoryWithMax. - // * Version 1.0 (20191205) of the WebAssembly spec allows at most one memory per module. + // - This is allowed to grow to (4GiB) limited by api.MemorySizer. To bound it, use ExportMemoryWithMax. + // - Version 1.0 (20191205) of the WebAssembly spec allows at most one memory per module. // // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#memory-section%E2%91%A0 ExportMemory(name string, minPages uint32) ModuleBuilder @@ -208,11 +208,11 @@ type ModuleBuilder interface { // ExportFunction("hello", hello). // Instantiate(ctx, r) // - // Notes + // # Notes // - // * Closing the Namespace has the same effect as closing the result. - // * Fields in the builder are copied during instantiation: Later changes do not affect the instantiated result. - // * To avoid using configuration defaults, use Compile instead. + // - Closing the Namespace has the same effect as closing the result. + // - Fields in the builder are copied during instantiation: Later changes do not affect the instantiated result. + // - To avoid using configuration defaults, use Compile instead. Instantiate(context.Context, Namespace) (api.Module, error) } diff --git a/config.go b/config.go index cff4236f..99bef862 100644 --- a/config.go +++ b/config.go @@ -31,10 +31,10 @@ type RuntimeConfig interface { // ("bulk-memory-operations"). This defaults to false as the feature was not finished in WebAssembly 1.0. // // Here are the notable effects: - // * Adds `memory.fill`, `memory.init`, `memory.copy` and `data.drop` instructions. - // * Adds `table.init`, `table.copy` and `elem.drop` instructions. - // * Introduces a "passive" form of element and data segments. - // * Stops checking "active" element and data segment boundaries at compile-time, meaning they can error at runtime. + // - Adds `memory.fill`, `memory.init`, `memory.copy` and `data.drop` instructions. + // - Adds `table.init`, `table.copy` and `elem.drop` instructions. + // - Introduces a "passive" form of element and data segments. + // - Stops checking "active" element and data segment boundaries at compile-time, meaning they can error at runtime. // // Note: "bulk-memory-operations" is mixed with the "reference-types" proposal // due to the WebAssembly Working Group merging them "mutually dependent". @@ -49,8 +49,8 @@ type RuntimeConfig interface { // finished in WebAssembly 1.0 (20191205). // // Here are the notable effects: - // * Function (`func`) types allow more than one result - // * Block types (`block`, `loop` and `if`) can be arbitrary function types + // - Function (`func`) types allow more than one result + // - Block types (`block`, `loop` and `if`) can be arbitrary function types // // See https://github.com/WebAssembly/spec/blob/main/proposals/multi-value/Overview.md WithFeatureMultiValue(bool) RuntimeConfig @@ -66,22 +66,22 @@ type RuntimeConfig interface { // ("nontrapping-float-to-int-conversion"). This defaults to false as the feature was not in WebAssembly 1.0. // // The only effect of enabling is allowing the following instructions, which return 0 on NaN instead of panicking. - // * `i32.trunc_sat_f32_s` - // * `i32.trunc_sat_f32_u` - // * `i32.trunc_sat_f64_s` - // * `i32.trunc_sat_f64_u` - // * `i64.trunc_sat_f32_s` - // * `i64.trunc_sat_f32_u` - // * `i64.trunc_sat_f64_s` - // * `i64.trunc_sat_f64_u` + // - `i32.trunc_sat_f32_s` + // - `i32.trunc_sat_f32_u` + // - `i32.trunc_sat_f64_s` + // - `i32.trunc_sat_f64_u` + // - `i64.trunc_sat_f32_s` + // - `i64.trunc_sat_f32_u` + // - `i64.trunc_sat_f64_s` + // - `i64.trunc_sat_f64_u` // // See https://github.com/WebAssembly/spec/blob/main/proposals/nontrapping-float-to-int-conversion/Overview.md WithFeatureNonTrappingFloatToIntConversion(bool) RuntimeConfig // WithFeatureReferenceTypes enables various instructions and features related to table and new reference types. // - // * Introduction of new value types: `funcref` and `externref`. - // * Support for the following new instructions: + // - Introduction of new value types: `funcref` and `externref`. + // - Support for the following new instructions: // * `ref.null` // * `ref.func` // * `ref.is_null` @@ -90,7 +90,7 @@ type RuntimeConfig interface { // * `table.grow` // * `table.set` // * `table.size` - // * Support for multiple tables per module: + // - Support for multiple tables per module: // * `call_indirect`, `table.init`, `table.copy` and `elem.drop` instructions can take non-zero table index. // * Element segments can take non-zero table index. // @@ -107,7 +107,7 @@ type RuntimeConfig interface { // as the feature was not in WebAssembly 1.0. // // Here are the notable effects: - // * Adds instructions `i32.extend8_s`, `i32.extend16_s`, `i64.extend8_s`, `i64.extend16_s` and `i64.extend32_s` + // - Adds instructions `i32.extend8_s`, `i32.extend16_s`, `i64.extend8_s`, `i64.extend16_s` and `i64.extend32_s` // // See https://github.com/WebAssembly/spec/blob/main/proposals/sign-extension-ops/Overview.md WithFeatureSignExtensionOps(bool) RuntimeConfig @@ -451,11 +451,11 @@ type ModuleConfig interface { // WithStartFunctions configures the functions to call after the module is // instantiated. Defaults to "_start". // - // Notes + // # Notes // - // * If any function doesn't exist, it is skipped. However, all functions + // - If any function doesn't exist, it is skipped. However, all functions // that do exist are called in order. - // * Some start functions may exit the module during instantiate with a + // - Some start functions may exit the module during instantiate with a // sys.ExitError (ex. emscripten), preventing use of exported functions. WithStartFunctions(...string) ModuleConfig @@ -464,10 +464,10 @@ type ModuleConfig interface { // This writer is most commonly used by the functions like "fd_write" in "wasi_snapshot_preview1" although it could // be used by functions imported from other modules. // - // Notes + // # Notes // - // * The caller is responsible to close any io.Writer they supply: It is not closed on api.Module Close. - // * This does not default to os.Stderr as that both violates sandboxing and prevents concurrent modules. + // - The caller is responsible to close any io.Writer they supply: It is not closed on api.Module Close. + // - This does not default to os.Stderr as that both violates sandboxing and prevents concurrent modules. // // See https://linux.die.net/man/3/stderr WithStderr(io.Writer) ModuleConfig @@ -477,10 +477,10 @@ type ModuleConfig interface { // This reader is most commonly used by the functions like "fd_read" in "wasi_snapshot_preview1" although it could // be used by functions imported from other modules. // - // Notes + // # Notes // - // * The caller is responsible to close any io.Reader they supply: It is not closed on api.Module Close. - // * This does not default to os.Stdin as that both violates sandboxing and prevents concurrent modules. + // - The caller is responsible to close any io.Reader they supply: It is not closed on api.Module Close. + // - This does not default to os.Stdin as that both violates sandboxing and prevents concurrent modules. // // See https://linux.die.net/man/3/stdin WithStdin(io.Reader) ModuleConfig @@ -490,10 +490,10 @@ type ModuleConfig interface { // This writer is most commonly used by the functions like "fd_write" in "wasi_snapshot_preview1" although it could // be used by functions imported from other modules. // - // Notes + // # Notes // - // * The caller is responsible to close any io.Writer they supply: It is not closed on api.Module Close. - // * This does not default to os.Stdout as that both violates sandboxing and prevents concurrent modules. + // - The caller is responsible to close any io.Writer they supply: It is not closed on api.Module Close. + // - This does not default to os.Stdout as that both violates sandboxing and prevents concurrent modules. // // See https://linux.die.net/man/3/stdout WithStdout(io.Writer) ModuleConfig @@ -528,11 +528,11 @@ type ModuleConfig interface { // return clock.nanotime() // }, sys.ClockResolution(time.Microsecond.Nanoseconds())) // - // Notes: - // * This does not default to time.Since as that violates sandboxing. - // * Some compilers implement sleep by looping on sys.Nanotime (ex. Go). - // * If you set this, you should probably set WithNanosleep also. - // * Use WithSysNanotime for a usable implementation. + // # Notes: + // - This does not default to time.Since as that violates sandboxing. + // - Some compilers implement sleep by looping on sys.Nanotime (ex. Go). + // - If you set this, you should probably set WithNanosleep also. + // - Use WithSysNanotime for a usable implementation. WithNanotime(sys.Nanotime, sys.ClockResolution) ModuleConfig // WithSysNanotime uses time.Now for sys.Nanotime with a resolution of 1us. @@ -552,12 +552,12 @@ type ModuleConfig interface { // err := unix.ClockNanosleep(unix.CLOCK_MONOTONIC, 0, &rel, &remain) // --snip-- // - // Notes: - // * This primarily supports `poll_oneoff` for relative clock events. - // * This does not default to time.Sleep as that violates sandboxing. - // * Some compilers implement sleep by looping on sys.Nanotime (ex. Go). - // * If you set this, you should probably set WithNanotime also. - // * Use WithSysNanosleep for a usable implementation. + // # Notes: + // - This primarily supports `poll_oneoff` for relative clock events. + // - This does not default to time.Sleep as that violates sandboxing. + // - Some compilers implement sleep by looping on sys.Nanotime (ex. Go). + // - If you set this, you should probably set WithNanotime also. + // - Use WithSysNanosleep for a usable implementation. WithNanosleep(sys.Nanosleep) ModuleConfig // WithSysNanosleep uses time.Sleep for sys.Nanosleep. diff --git a/emscripten/emscripten.go b/emscripten/emscripten.go index 040b19e5..19f08e25 100644 --- a/emscripten/emscripten.go +++ b/emscripten/emscripten.go @@ -23,7 +23,7 @@ import ( // Instantiate instantiates the "env" module used by Emscripten into the // runtime default namespace. // -// Notes +// # Notes // // - Closing the wazero.Runtime has the same effect as closing the result. // - To add more functions to the "env" module, use FunctionExporter. diff --git a/emscripten/emscripten_test.go b/emscripten/emscripten_test.go index 91de67df..e46f2401 100644 --- a/emscripten/emscripten_test.go +++ b/emscripten/emscripten_test.go @@ -8,6 +8,7 @@ import ( "github.com/tetratelabs/wazero" . "github.com/tetratelabs/wazero/experimental" + "github.com/tetratelabs/wazero/experimental/logging" "github.com/tetratelabs/wazero/internal/testing/require" "github.com/tetratelabs/wazero/sys" "github.com/tetratelabs/wazero/wasi_snapshot_preview1" @@ -26,7 +27,7 @@ func TestGrow(t *testing.T) { var log bytes.Buffer // Set context to one that has an experimental listener - ctx := context.WithValue(testCtx, FunctionListenerFactoryKey{}, NewLoggingListenerFactory(&log)) + ctx := context.WithValue(testCtx, FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&log)) r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfigInterpreter()) defer r.Close(ctx) diff --git a/experimental/listener.go b/experimental/listener.go index 36dca873..29fe77ea 100644 --- a/experimental/listener.go +++ b/experimental/listener.go @@ -27,21 +27,23 @@ type FunctionListener interface { // Before is invoked before a function is called. The returned context will // be used as the context of this function call. // - // Params + // # Params // - // * ctx - the context of the caller function which must be the same - // instance or parent of the result. - // * paramValues - api.ValueType encoded parameters. - Before(ctx context.Context, paramValues []uint64) context.Context + // - ctx: the context of the caller function which must be the same + // instance or parent of the result. + // - def: the function definition. + // - paramValues: api.ValueType encoded parameters. + Before(ctx context.Context, def api.FunctionDefinition, paramValues []uint64) context.Context // After is invoked after a function is called. // - // Params + // # Params // - // * ctx - the context returned by Before. - // * err - nil if the function didn't err - // * resultValues - api.ValueType encoded results. - After(ctx context.Context, err error, resultValues []uint64) + // - ctx: the context returned by Before. + // - def: the function definition. + // - err: nil if the function didn't err + // - resultValues: api.ValueType encoded results. + After(ctx context.Context, def api.FunctionDefinition, err error, resultValues []uint64) } // TODO: We need to add tests to enginetest to ensure contexts nest. A good test can use a combination of call and call diff --git a/experimental/listener_example_test.go b/experimental/listener_example_test.go new file mode 100644 index 00000000..5fe921e3 --- /dev/null +++ b/experimental/listener_example_test.go @@ -0,0 +1,92 @@ +package experimental_test + +import ( + "context" + _ "embed" + "fmt" + "log" + "sort" + + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/api" + . "github.com/tetratelabs/wazero/experimental" + "github.com/tetratelabs/wazero/wasi_snapshot_preview1" +) + +// listenerWasm was generated by the following: +// +// cd testdata; wat2wasm --debug-names listener.wat +// +//go:embed logging/testdata/listener.wasm +var listenerWasm []byte + +// uniqGoFuncs implements both FunctionListenerFactory and FunctionListener +type uniqGoFuncs map[string]struct{} + +// callees returns the go functions called. +func (u uniqGoFuncs) callees() []string { + ret := make([]string, 0, len(u)) + for k := range u { + ret = append(ret, k) + } + // Sort names for consistent iteration + sort.Strings(ret) + return ret +} + +// NewListener implements FunctionListenerFactory.NewListener +func (u uniqGoFuncs) NewListener(def api.FunctionDefinition) FunctionListener { + if def.GoFunc() == nil { + return nil // only track go funcs + } + return u +} + +// Before implements FunctionListener.Before +func (u uniqGoFuncs) Before(ctx context.Context, def api.FunctionDefinition, _ []uint64) context.Context { + u[def.DebugName()] = struct{}{} + return ctx +} + +// After implements FunctionListener.After +func (u uniqGoFuncs) After(context.Context, api.FunctionDefinition, error, []uint64) {} + +// This shows how to make a listener that counts go function calls. +func Example_customListenerFactory() { + u := uniqGoFuncs{} + + // Set context to one that has an experimental listener + ctx := context.WithValue(context.Background(), FunctionListenerFactoryKey{}, u) + + r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfigInterpreter()) + defer r.Close(ctx) // This closes everything this Runtime created. + + if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil { + log.Panicln(err) + } + + // Compile the WebAssembly module using the default configuration. + code, err := r.CompileModule(ctx, listenerWasm, wazero.NewCompileConfig()) + if err != nil { + log.Panicln(err) + } + + mod, err := r.InstantiateModule(ctx, code, wazero.NewModuleConfig()) + if err != nil { + log.Panicln(err) + } + + for i := 0; i < 5; i++ { + if _, err = mod.ExportedFunction("rand").Call(ctx, 4); err != nil { + log.Panicln(err) + } + } + + // A Go function was called multiple times, but we should only see it once. + for _, f := range u.callees() { + fmt.Println(f) + } + + // Output: + // wasi_snapshot_preview1.random_get +} diff --git a/experimental/log_listener.go b/experimental/logging/log_listener.go similarity index 91% rename from experimental/log_listener.go rename to experimental/logging/log_listener.go index 93056dee..831a1df6 100644 --- a/experimental/log_listener.go +++ b/experimental/logging/log_listener.go @@ -1,4 +1,4 @@ -package experimental +package logging import ( "context" @@ -8,12 +8,13 @@ import ( "strings" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental" "github.com/tetratelabs/wazero/internal/wasi_snapshot_preview1" ) // NewLoggingListenerFactory implements FunctionListenerFactory to log all // functions that have a name to the writer. -func NewLoggingListenerFactory(writer io.Writer) FunctionListenerFactory { +func NewLoggingListenerFactory(writer io.Writer) experimental.FunctionListenerFactory { return &loggingListenerFactory{writer} } @@ -21,7 +22,7 @@ type loggingListenerFactory struct{ writer io.Writer } // NewListener implements the same method as documented on // experimental.FunctionListener. -func (f *loggingListenerFactory) NewListener(fnd api.FunctionDefinition) FunctionListener { +func (f *loggingListenerFactory) NewListener(fnd api.FunctionDefinition) experimental.FunctionListener { return &loggingListener{writer: f.writer, fnd: fnd, isWasi: fnd.ModuleName() == "wasi_snapshot_preview1"} } @@ -39,7 +40,7 @@ type loggingListener struct { // Before logs to stdout the module and function name, prefixed with '-->' and // indented based on the call nesting level. -func (l *loggingListener) Before(ctx context.Context, vals []uint64) context.Context { +func (l *loggingListener) Before(ctx context.Context, _ api.FunctionDefinition, vals []uint64) context.Context { nestLevel, _ := ctx.Value(nestLevelKey{}).(int) l.writeIndented(true, nil, vals, nestLevel+1) @@ -50,7 +51,7 @@ func (l *loggingListener) Before(ctx context.Context, vals []uint64) context.Con // After logs to stdout the module and function name, prefixed with '<--' and // indented based on the call nesting level. -func (l *loggingListener) After(ctx context.Context, err error, vals []uint64) { +func (l *loggingListener) After(ctx context.Context, _ api.FunctionDefinition, err error, vals []uint64) { // Note: We use the nest level directly even though it is the "next" nesting level. // This works because our indent of zero nesting is one tab. l.writeIndented(false, err, vals, ctx.Value(nestLevelKey{}).(int)) diff --git a/experimental/log_listener_example_test.go b/experimental/logging/log_listener_example_test.go similarity index 84% rename from experimental/log_listener_example_test.go rename to experimental/logging/log_listener_example_test.go index c7d0f78d..7c1f4880 100644 --- a/experimental/log_listener_example_test.go +++ b/experimental/logging/log_listener_example_test.go @@ -1,4 +1,4 @@ -package experimental_test +package logging_test import ( "context" @@ -7,7 +7,8 @@ import ( "os" "github.com/tetratelabs/wazero" - . "github.com/tetratelabs/wazero/experimental" + "github.com/tetratelabs/wazero/experimental" + "github.com/tetratelabs/wazero/experimental/logging" "github.com/tetratelabs/wazero/wasi_snapshot_preview1" ) @@ -21,7 +22,7 @@ var listenerWasm []byte // This is a very basic integration of listener. The main goal is to show how it is configured. func Example_newLoggingListenerFactory() { // Set context to one that has an experimental listener - ctx := context.WithValue(context.Background(), FunctionListenerFactoryKey{}, NewLoggingListenerFactory(os.Stdout)) + ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(os.Stdout)) r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfigInterpreter()) defer r.Close(ctx) // This closes everything this Runtime created. diff --git a/experimental/log_listener_test.go b/experimental/logging/log_listener_test.go similarity index 91% rename from experimental/log_listener_test.go rename to experimental/logging/log_listener_test.go index f41c8939..531b7569 100644 --- a/experimental/log_listener_test.go +++ b/experimental/logging/log_listener_test.go @@ -1,18 +1,22 @@ -package experimental_test +package logging_test import ( "bytes" + "context" "io" "math" "testing" "github.com/tetratelabs/wazero/api" - "github.com/tetratelabs/wazero/experimental" + "github.com/tetratelabs/wazero/experimental/logging" "github.com/tetratelabs/wazero/internal/testing/require" "github.com/tetratelabs/wazero/internal/wasm" "github.com/tetratelabs/wazero/wasi_snapshot_preview1" ) +// testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors. +var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary") + func Test_loggingListener(t *testing.T) { wasiFuncName := "random_get" wasiFuncType := &wasm.FunctionType{ @@ -261,7 +265,7 @@ func Test_loggingListener(t *testing.T) { } var out bytes.Buffer - lf := experimental.NewLoggingListenerFactory(&out) + lf := logging.NewLoggingListenerFactory(&out) fn := func() {} for _, tt := range tests { tc := tt @@ -288,11 +292,12 @@ func Test_loggingListener(t *testing.T) { m.CodeSection = []*wasm.Code{{Body: []byte{wasm.OpcodeEnd}}} } m.BuildFunctionDefinitions() + def := m.FunctionDefinitionSection[0] l := lf.NewListener(m.FunctionDefinitionSection[0]) out.Reset() - ctx := l.Before(testCtx, tc.params) - l.After(ctx, tc.err, tc.results) + ctx := l.Before(testCtx, def, tc.params) + l.After(ctx, def, tc.err, tc.results) require.Equal(t, tc.expected, out.String()) }) } @@ -311,7 +316,7 @@ func toNameMap(names []string) wasm.NameMap { func Test_loggingListener_indentation(t *testing.T) { out := bytes.NewBuffer(nil) - lf := experimental.NewLoggingListenerFactory(out) + lf := logging.NewLoggingListenerFactory(out) m := &wasm.Module{ TypeSection: []*wasm.FunctionType{{}}, FunctionSection: []wasm.Index{0, 0}, @@ -322,13 +327,15 @@ func Test_loggingListener_indentation(t *testing.T) { }, } m.BuildFunctionDefinitions() - l1 := lf.NewListener(m.FunctionDefinitionSection[0]) - l2 := lf.NewListener(m.FunctionDefinitionSection[1]) + def1 := m.FunctionDefinitionSection[0] + l1 := lf.NewListener(def1) + def2 := m.FunctionDefinitionSection[1] + l2 := lf.NewListener(def2) - ctx := l1.Before(testCtx, []uint64{}) - ctx1 := l2.Before(ctx, []uint64{}) - l2.After(ctx1, nil, []uint64{}) - l1.After(ctx, nil, []uint64{}) + ctx := l1.Before(testCtx, def1, []uint64{}) + ctx1 := l2.Before(ctx, def2, []uint64{}) + l2.After(ctx1, def2, nil, []uint64{}) + l1.After(ctx, def1, nil, []uint64{}) require.Equal(t, `--> test.fn1() --> test.fn2() <-- () diff --git a/experimental/testdata/listener.wasm b/experimental/logging/testdata/listener.wasm similarity index 100% rename from experimental/testdata/listener.wasm rename to experimental/logging/testdata/listener.wasm diff --git a/experimental/testdata/listener.wat b/experimental/logging/testdata/listener.wat similarity index 100% rename from experimental/testdata/listener.wat rename to experimental/logging/testdata/listener.wat diff --git a/internal/asm/arm64/consts.go b/internal/asm/arm64/consts.go index f4c88fc1..fbd61265 100644 --- a/internal/asm/arm64/consts.go +++ b/internal/asm/arm64/consts.go @@ -692,9 +692,9 @@ const ( // VCNT is the CNT instruction. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/CNT--vector- VCNT // VMOV has different semantics depending on the types of operands: - // * LDR(SIMD&FP) if the src is memory and dst is a vector: https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/LDR--immediate--SIMD-FP---Load-SIMD-FP-Register--immediate-offset-- - // * LDR(literal, SIMD&FP) if the src is static const and dst is a vector: https://developer.arm.com/documentation/dui0801/h/A64-Floating-point-Instructions/LDR--literal--SIMD-and-FP- - // * STR(SIMD&FP) if the dst is memory and src is a vector: https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/STR--immediate--SIMD-FP---Store-SIMD-FP-register--immediate-offset-- + // - LDR(SIMD&FP) if the src is memory and dst is a vector: https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/LDR--immediate--SIMD-FP---Load-SIMD-FP-Register--immediate-offset-- + // - LDR(literal, SIMD&FP) if the src is static const and dst is a vector: https://developer.arm.com/documentation/dui0801/h/A64-Floating-point-Instructions/LDR--literal--SIMD-and-FP- + // - STR(SIMD&FP) if the dst is memory and src is a vector: https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/STR--immediate--SIMD-FP---Store-SIMD-FP-register--immediate-offset-- VMOV // UMOV is the UMOV instruction https://developer.arm.com/documentation/ddi0596/2021-12/SIMD-FP-Instructions/UMOV--Unsigned-Move-vector-element-to-general-purpose-register-?lang=en UMOV diff --git a/internal/engine/interpreter/interpreter.go b/internal/engine/interpreter/interpreter.go index 72224004..d22cd4be 100644 --- a/internal/engine/interpreter/interpreter.go +++ b/internal/engine/interpreter/interpreter.go @@ -811,7 +811,7 @@ func (ce *callEngine) callFunction(ctx context.Context, callCtx *wasm.CallContex func (ce *callEngine) callGoFunc(ctx context.Context, callCtx *wasm.CallContext, f *function, params []uint64) (results []uint64) { callCtx = callCtx.WithMemory(ce.callerMemory()) if f.source.FunctionListener != nil { - ctx = f.source.FunctionListener.Before(ctx, params) + ctx = f.source.FunctionListener.Before(ctx, f.source.Definition(), params) } frame := &callFrame{f: f} ce.pushFrame(frame) @@ -819,7 +819,7 @@ func (ce *callEngine) callGoFunc(ctx context.Context, callCtx *wasm.CallContext, ce.popFrame() if f.source.FunctionListener != nil { // TODO: This doesn't get the error due to use of panic to propagate them. - f.source.FunctionListener.After(ctx, nil, results) + f.source.FunctionListener.After(ctx, f.source.Definition(), nil, results) } return } @@ -4299,10 +4299,10 @@ func i32Abs(v uint32) uint32 { } func (ce *callEngine) callNativeFuncWithListener(ctx context.Context, callCtx *wasm.CallContext, f *function, fnl experimental.FunctionListener) context.Context { - ctx = fnl.Before(ctx, ce.peekValues(len(f.source.Type.Params))) + ctx = fnl.Before(ctx, f.source.Definition(), ce.peekValues(len(f.source.Type.Params))) ce.callNativeFunc(ctx, callCtx, f) // TODO: This doesn't get the error due to use of panic to propagate them. - fnl.After(ctx, nil, ce.peekValues(len(f.source.Type.Results))) + fnl.After(ctx, f.source.Definition(), nil, ce.peekValues(len(f.source.Type.Results))) return ctx } diff --git a/internal/engine/interpreter/interpreter_test.go b/internal/engine/interpreter/interpreter_test.go index c395d7af..e9d820db 100644 --- a/internal/engine/interpreter/interpreter_test.go +++ b/internal/engine/interpreter/interpreter_test.go @@ -10,6 +10,7 @@ import ( "unsafe" "github.com/tetratelabs/wazero/experimental" + "github.com/tetratelabs/wazero/experimental/logging" "github.com/tetratelabs/wazero/internal/buildoptions" "github.com/tetratelabs/wazero/internal/testing/enginetest" "github.com/tetratelabs/wazero/internal/testing/require" @@ -65,7 +66,7 @@ func TestInterpreter_CallEngine_PushFrame_StackOverflow(t *testing.T) { // et is used for tests defined in the enginetest package. var et = &engineTester{} var functionLog bytes.Buffer -var listenerFactory = experimental.NewLoggingListenerFactory(&functionLog) +var listenerFactory = logging.NewLoggingListenerFactory(&functionLog) // engineTester implements enginetest.EngineTester. type engineTester struct{} diff --git a/internal/wasm/call_context.go b/internal/wasm/call_context.go index 152805b2..88b73320 100644 --- a/internal/wasm/call_context.go +++ b/internal/wasm/call_context.go @@ -38,10 +38,10 @@ type CallContext struct { // Sys is exposed for use in special imports such as WASI, assemblyscript // and wasm_exec. // - // Notes + // # Notes // - // * This is a part of CallContext so that scope and Close is coherent. - // * This is not exposed outside this repository (as a host function + // - This is a part of CallContext so that scope and Close is coherent. + // - This is not exposed outside this repository (as a host function // parameter) because we haven't thought through capabilities based // security implications. Sys *internalsys.Context diff --git a/internal/wasm/module.go b/internal/wasm/module.go index 1eeafa76..a4e35020 100644 --- a/internal/wasm/module.go +++ b/internal/wasm/module.go @@ -579,7 +579,7 @@ func (m *Module) buildGlobals(importedGlobals []*GlobalInstance) (globals []*Glo // BuildFunctions generates function instances for all host or wasm-defined // functions in this module. // -// Notes +// # Notes // - This relies on data generated by Module.BuildFunctionDefinitions. // - This is exported for tests that don't call Instantiate, notably only // enginetest.go. diff --git a/namespace.go b/namespace.go index dbf97e19..d24219f7 100644 --- a/namespace.go +++ b/namespace.go @@ -22,9 +22,9 @@ type Namespace interface { // module, _ := n.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("prod")) // // While CompiledModule is pre-validated, there are a few situations which can cause an error: - // * The module name is already in use. - // * The module has a table element initializer that resolves to an index outside the Table minimum size. - // * The module has a start function, and it failed to execute. + // - The module name is already in use. + // - The module has a table element initializer that resolves to an index outside the Table minimum size. + // - The module has a start function, and it failed to execute. InstantiateModule(ctx context.Context, compiled CompiledModule, config ModuleConfig) (api.Module, error) // CloseWithExitCode closes all modules initialized in this Namespace with the provided exit code. diff --git a/runtime.go b/runtime.go index 78f115d9..33884f6e 100644 --- a/runtime.go +++ b/runtime.go @@ -36,13 +36,13 @@ type Runtime interface { // Any pre-compilation done after decoding wasm is dependent on RuntimeConfig or CompileConfig. // // There are two main reasons to use CompileModule instead of InstantiateModuleFromBinary: - // * Improve performance when the same module is instantiated multiple times under different names - // * Reduce the amount of errors that can occur during InstantiateModule. + // - Improve performance when the same module is instantiated multiple times under different names + // - Reduce the amount of errors that can occur during InstantiateModule. // - // Notes + // # Notes // - // * The resulting module name defaults to what was binary from the custom name section. - // * Any pre-compilation done after decoding the source is dependent on RuntimeConfig or CompileConfig. + // - The resulting module name defaults to what was binary from the custom name section. + // - Any pre-compilation done after decoding the source is dependent on RuntimeConfig or CompileConfig. // // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#name-section%E2%91%A0 CompileModule(ctx context.Context, binary []byte, config CompileConfig) (CompiledModule, error) @@ -57,11 +57,11 @@ type Runtime interface { // // module, _ := r.InstantiateModuleFromBinary(ctx, wasm) // - // Notes + // # Notes // - // * This is a convenience utility that chains CompileModule with InstantiateModule. To instantiate the same + // - This is a convenience utility that chains CompileModule with InstantiateModule. To instantiate the same // source multiple times, use CompileModule as InstantiateModule avoids redundant decoding and/or compilation. - // * To avoid using configuration defaults, use InstantiateModule instead. + // - To avoid using configuration defaults, use InstantiateModule instead. InstantiateModuleFromBinary(ctx context.Context, source []byte) (api.Module, error) // Namespace is the default namespace of this runtime, and is embedded for convenience. Most users will only use the @@ -94,11 +94,11 @@ type Runtime interface { // _ = b.WithTraceToStdout().InstantiateModule(ctx, ns2) // m2, _ := ns2.InstantiateModule(ctx, compiled, config) // - // Notes + // # Notes // - // * The returned namespace does not inherit any modules from the runtime default namespace. - // * Closing the returned namespace closes any modules in it. - // * Closing this runtime also closes the namespace returned from this function. + // - The returned namespace does not inherit any modules from the runtime default namespace. + // - Closing the returned namespace closes any modules in it. + // - Closing this runtime also closes the namespace returned from this function. NewNamespace(context.Context) Namespace // CloseWithExitCode closes all the modules that have been initialized in this Runtime with the provided exit code. diff --git a/wasi_snapshot_preview1/args.go b/wasi_snapshot_preview1/args.go index 2d7636ec..8d80774a 100644 --- a/wasi_snapshot_preview1/args.go +++ b/wasi_snapshot_preview1/args.go @@ -15,7 +15,7 @@ const ( // argsGet is the WASI function named functionArgsGet that reads command-line // argument data. // -// Parameters +// # Parameters // // - argv: offset to begin writing argument offsets in uint32 little-endian // encoding to api.Memory @@ -58,7 +58,7 @@ var argsGet = wasm.NewGoFunc( // argsSizesGet is the WASI function named functionArgsSizesGet that reads // command-line argument sizes. // -// Parameters +// # Parameters // // - resultArgc: offset to write the argument count to api.Memory // - resultArgvBufSize: offset to write the null-terminated argument length to diff --git a/wasi_snapshot_preview1/clock.go b/wasi_snapshot_preview1/clock.go index 70dab67c..c4297ea0 100644 --- a/wasi_snapshot_preview1/clock.go +++ b/wasi_snapshot_preview1/clock.go @@ -28,7 +28,7 @@ const ( // clockResGet is the WASI function named functionClockResGet that returns the // resolution of time values returned by clockTimeGet. // -// Parameters +// # Parameters // // - id: clock ID to use // - resultResolution: offset to write the resolution to api.Memory @@ -83,7 +83,7 @@ var clockResGet = wasm.NewGoFunc( // clockTimeGet is the WASI function named functionClockTimeGet that returns // the time value of a name (time.Now). // -// Parameters +// # Parameters // // - id: clock ID to use // - precision: maximum lag (exclusive) that the returned time value may have, diff --git a/wasi_snapshot_preview1/environ.go b/wasi_snapshot_preview1/environ.go index d168e8e6..52404924 100644 --- a/wasi_snapshot_preview1/environ.go +++ b/wasi_snapshot_preview1/environ.go @@ -15,7 +15,7 @@ const ( // environGet is the WASI function named functionEnvironGet that reads // environment variables. // -// Parameters +// # Parameters // // - environ: offset to begin writing environment offsets in uint32 // little-endian encoding to api.Memory @@ -58,7 +58,7 @@ var environGet = wasm.NewGoFunc( // environSizesGet is the WASI function named functionEnvironSizesGet that // reads environment variable sizes. // -// Parameters +// # Parameters // // - resultEnvironc: offset to write the count of environment variables to // api.Memory diff --git a/wasi_snapshot_preview1/errno.go b/wasi_snapshot_preview1/errno.go index d48bdd3d..6113e0a0 100644 --- a/wasi_snapshot_preview1/errno.go +++ b/wasi_snapshot_preview1/errno.go @@ -6,7 +6,7 @@ import ( // Errno are the error codes returned by WASI functions. // -// Notes +// # Notes // // - This is not always an error, as ErrnoSuccess is a valid code. // - Codes are defined even when not relevant to WASI for use in higher-level libraries or alignment with POSIX. diff --git a/wasi_snapshot_preview1/fs.go b/wasi_snapshot_preview1/fs.go index 06872684..4b4cfecf 100644 --- a/wasi_snapshot_preview1/fs.go +++ b/wasi_snapshot_preview1/fs.go @@ -69,7 +69,7 @@ var fdAllocate = stubFunction( // fdClose is the WASI function named functionFdClose which closes a file // descriptor. // -// Parameters +// # Parameters // // - fd: file descriptor to close // @@ -107,7 +107,7 @@ var fdDatasync = stubFunction( // fdFdstatGet is the WASI function named functionFdFdstatGet which returns the // attributes of a file descriptor. // -// Parameters +// # Parameters // // - fd: file descriptor to get the fdstat attributes data // - resultFdstat: offset to write the result fdstat data @@ -219,7 +219,7 @@ var fdPread = stubFunction( // fdPrestatGet is the WASI function named functionFdPrestatGet which returns // the prestat data of a file descriptor. // -// Parameters +// # Parameters // // - fd: file descriptor to get the prestat // - resultPrestat: offset to write the result prestat data @@ -273,7 +273,7 @@ var fdPrestatGet = wasm.NewGoFunc( // fdPrestatDirName is the WASI function named functionFdPrestatDirName which // returns the path of the pre-opened directory of a file descriptor. // -// Parameters +// # Parameters // // - fd: file descriptor to get the path of the pre-opened directory // - path: offset in api.Memory to write the result path @@ -289,7 +289,7 @@ var fdPrestatGet = wasm.NewGoFunc( // - ErrnoNametoolong: `pathLen` is longer than the actual length of the result // // For example, the directory name corresponding with `fd` was "/tmp" and -// parameters path=1 pathLen=4 (correct), this function will write the below to +// # Parameters path=1 pathLen=4 (correct), this function will write the below to // api.Memory: // // pathLen @@ -335,7 +335,7 @@ var fdPwrite = stubFunction(functionFdPwrite, // fdRead is the WASI function named functionFdRead which reads from a file // descriptor. // -// Parameters +// # Parameters // // - fd: an opened file descriptor to read data from // - iovs: offset in api.Memory to read offset, size pairs representing where @@ -444,7 +444,7 @@ var fdRenumber = stubFunction( // fdSeek is the WASI function named functionFdSeek which moves the offset of a // file descriptor. // -// Parameters +// # Parameters // // - fd: file descriptor to move the offset of // - offset: signed int64, which is encoded as uint64, input argument to @@ -531,7 +531,7 @@ var fdTell = stubFunction( // fdWrite is the WASI function named functionFdWrite which writes to a file // descriptor. // -// Parameters +// # Parameters // // - fd: an opened file descriptor to write data to // - iovs: offset in api.Memory to read offset, size pairs representing the @@ -668,7 +668,7 @@ var pathLink = stubFunction( // pathOpen is the WASI function named functionPathOpen which opens a file or // directory. This returns ErrnoBadf if the fd is invalid. // -// Parameters +// # Parameters // // - fd: file descriptor of a directory that `path` is relative to // - dirflags: flags to indicate how to resolve `path` @@ -713,7 +713,7 @@ var pathLink = stubFunction( // []byte{ 0..6, ?, 5, 0, 0, 0, ?} // resultOpenedFd --^ // -// Notes +// # Notes // - This is similar to `openat` in POSIX. https://linux.die.net/man/3/openat // - The returned file descriptor is not guaranteed to be the lowest-number // - Rights will never be implemented per https://github.com/WebAssembly/WASI/issues/469#issuecomment-1045251844 diff --git a/wasi_snapshot_preview1/poll.go b/wasi_snapshot_preview1/poll.go index c66a8d8c..b5603fef 100644 --- a/wasi_snapshot_preview1/poll.go +++ b/wasi_snapshot_preview1/poll.go @@ -24,7 +24,7 @@ const ( // pollOneoff is the WASI function named functionPollOneoff that concurrently // polls for the occurrence of a set of events. // -// Parameters +// # Parameters // // - in: pointer to the subscriptions (48 bytes each) // - out: pointer to the resulting events (32 bytes each) @@ -39,7 +39,7 @@ const ( // - ErrnoFault: there is not enough memory to read the subscriptions or // write results. // -// Notes +// # Notes // // - Since the `out` pointer nests Errno, the result is always ErrnoSuccess. // - importPollOneoff shows this signature in the WebAssembly 1.0 Text Format. diff --git a/wasi_snapshot_preview1/proc.go b/wasi_snapshot_preview1/proc.go index f87da941..1026d172 100644 --- a/wasi_snapshot_preview1/proc.go +++ b/wasi_snapshot_preview1/proc.go @@ -17,7 +17,7 @@ const ( // execution of the module with an exit code. The only successful exit code is // zero. // -// Parameters +// # Parameters // // - exitCode: exit code. // diff --git a/wasi_snapshot_preview1/random.go b/wasi_snapshot_preview1/random.go index 67513d0d..885640ac 100644 --- a/wasi_snapshot_preview1/random.go +++ b/wasi_snapshot_preview1/random.go @@ -13,7 +13,7 @@ const functionRandomGet = "random_get" // randomGet is the WASI function named functionRandomGet which writes random // data to a buffer. // -// Parameters +// # Parameters // // - buf: api.Memory offset to write random values // - bufLen: size of random data in bytes diff --git a/wasi_snapshot_preview1/wasi.go b/wasi_snapshot_preview1/wasi.go index 09c96bba..b65b33ce 100644 --- a/wasi_snapshot_preview1/wasi.go +++ b/wasi_snapshot_preview1/wasi.go @@ -33,7 +33,7 @@ const i32, i64 = wasm.ValueTypeI32, wasm.ValueTypeI64 // Instantiate instantiates the ModuleName module into the runtime default // namespace. // -// Notes +// # Notes // // - Closing the wazero.Runtime has the same effect as closing the result. // - To instantiate into another wazero.Namespace, use NewBuilder instead. diff --git a/wasi_snapshot_preview1/wasi_test.go b/wasi_snapshot_preview1/wasi_test.go index e8a1ba2b..11254e21 100644 --- a/wasi_snapshot_preview1/wasi_test.go +++ b/wasi_snapshot_preview1/wasi_test.go @@ -11,6 +11,7 @@ import ( "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" . "github.com/tetratelabs/wazero/experimental" + "github.com/tetratelabs/wazero/experimental/logging" "github.com/tetratelabs/wazero/internal/testing/require" ) @@ -36,7 +37,7 @@ func requireModule(t *testing.T, config wazero.ModuleConfig) (api.Module, api.Cl var log bytes.Buffer // Set context to one that has an experimental listener - ctx := context.WithValue(testCtx, FunctionListenerFactoryKey{}, NewLoggingListenerFactory(&log)) + ctx := context.WithValue(testCtx, FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&log)) r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfigInterpreter()) @@ -57,7 +58,7 @@ func requireErrnoNosys(t *testing.T, funcName string, params ...uint64) string { var log bytes.Buffer // Set context to one that has an experimental listener - ctx := context.WithValue(testCtx, FunctionListenerFactoryKey{}, NewLoggingListenerFactory(&log)) + ctx := context.WithValue(testCtx, FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&log)) r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfigInterpreter()) defer r.Close(ctx)