-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathdecorator.go
107 lines (90 loc) · 2.81 KB
/
decorator.go
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
package wazergo
import (
"context"
"fmt"
"io"
"log"
"strings"
. "github.com/stealthrocket/wazergo/types"
"github.com/tetratelabs/wazero/api"
)
// Decorator is an interface type which applies a transformation to a function.
type Decorator[T Module] interface {
Decorate(module string, fn Function[T]) Function[T]
}
// DecoratorFunc is a helper used to create decorators from functions using type
// inference to keep the syntax simple.
func DecoratorFunc[T Module](d func(string, Function[T]) Function[T]) Decorator[T] {
return decoratorFunc[T](d)
}
type decoratorFunc[T Module] func(string, Function[T]) Function[T]
func (d decoratorFunc[T]) Decorate(module string, fn Function[T]) Function[T] { return d(module, fn) }
// Log constructs a function decorator which adds logging to function calls.
func Log[T Module](logger *log.Logger) Decorator[T] {
return DecoratorFunc(func(module string, fn Function[T]) Function[T] {
if logger == nil {
return fn
}
n := fn.NumParams()
return fn.WithFunc(func(this T, ctx context.Context, module api.Module, stack []uint64) {
params := make([]uint64, n)
copy(params, stack)
panicked := true
defer func() {
memory := module.Memory()
buffer := new(strings.Builder)
defer logger.Printf("%s", buffer)
fmt.Fprintf(buffer, "%s::%s(", module, fn.Name)
formatValues(buffer, memory, params, fn.Params)
fmt.Fprintf(buffer, ")")
if panicked {
fmt.Fprintf(buffer, " PANIC!")
} else {
fmt.Fprintf(buffer, " → ")
formatValues(buffer, memory, stack, fn.Results)
}
}()
fn.Func(this, ctx, module, stack)
panicked = false
})
})
}
func formatValues(w io.Writer, memory api.Memory, stack []uint64, values []Value) {
for i, v := range values {
if i > 0 {
fmt.Fprintf(w, ", ")
}
v.FormatValue(w, memory, stack)
stack = stack[len(v.ValueTypes()):]
}
}
// Decorate returns a version of the given host module where the decorators were
// applied to all its functions.
func Decorate[T Module](mod HostModule[T], decorators ...Decorator[T]) HostModule[T] {
functions := mod.Functions()
decorated := &decoratedHostModule[T]{
hostModule: mod,
functions: make(Functions[T], len(functions)),
}
moduleName := mod.Name()
for name, function := range functions {
for _, decorator := range decorators {
function = decorator.Decorate(moduleName, function)
}
decorated.functions[name] = function
}
return decorated
}
type decoratedHostModule[T Module] struct {
hostModule HostModule[T]
functions Functions[T]
}
func (m *decoratedHostModule[T]) Name() string {
return m.hostModule.Name()
}
func (m *decoratedHostModule[T]) Functions() Functions[T] {
return m.functions
}
func (m *decoratedHostModule[T]) Instantiate(ctx context.Context, options ...Option[T]) (T, error) {
return m.hostModule.Instantiate(ctx, options...)
}