1 package wasm
2
3 import (
4 "context"
5 "testing"
6
7 "github.com/tetratelabs/wazero/api"
8 "github.com/tetratelabs/wazero/internal/testing/require"
9 . "github.com/tetratelabs/wazero/internal/wasip1"
10 )
11
12 func argsSizesGet(ctx context.Context, mod api.Module, resultArgc, resultArgvBufSize uint32) uint32 {
13 return 0
14 }
15
16 func fdWrite(ctx context.Context, mod api.Module, fd, iovs, iovsCount, resultSize uint32) uint32 {
17 return 0
18 }
19
20 func swap(ctx context.Context, x, y uint32) (uint32, uint32) {
21 return y, x
22 }
23
24 func TestNewHostModule(t *testing.T) {
25 t.Run("empty name not allowed", func(t *testing.T) {
26 _, err := NewHostModule("", nil, nil, api.CoreFeaturesV2)
27 require.Error(t, err)
28 })
29
30 swapName := "swap"
31 tests := []struct {
32 name, moduleName string
33 exportNames []string
34 nameToHostFunc map[string]*HostFunc
35 expected *Module
36 }{
37 {
38 name: "only name",
39 moduleName: "test",
40 expected: &Module{NameSection: &NameSection{ModuleName: "test"}},
41 },
42 {
43 name: "funcs",
44 moduleName: InternalModuleName,
45 exportNames: []string{ArgsSizesGetName, FdWriteName},
46 nameToHostFunc: map[string]*HostFunc{
47 ArgsSizesGetName: {
48 ExportName: ArgsSizesGetName,
49 ParamNames: []string{"result.argc", "result.argv_len"},
50 ResultNames: []string{"errno"},
51 Code: Code{GoFunc: argsSizesGet},
52 },
53 FdWriteName: {
54 ExportName: FdWriteName,
55 ParamNames: []string{"fd", "iovs", "iovs_len", "result.size"},
56 ResultNames: []string{"errno"},
57 Code: Code{GoFunc: fdWrite},
58 },
59 },
60 expected: &Module{
61 TypeSection: []FunctionType{
62 {Params: []ValueType{i32, i32}, Results: []ValueType{i32}},
63 {Params: []ValueType{i32, i32, i32, i32}, Results: []ValueType{i32}},
64 },
65 FunctionSection: []Index{0, 1},
66 CodeSection: []Code{MustParseGoReflectFuncCode(argsSizesGet), MustParseGoReflectFuncCode(fdWrite)},
67 ExportSection: []Export{
68 {Name: ArgsSizesGetName, Type: ExternTypeFunc, Index: 0},
69 {Name: FdWriteName, Type: ExternTypeFunc, Index: 1},
70 },
71 Exports: map[string]*Export{
72 ArgsSizesGetName: {Name: ArgsSizesGetName, Type: ExternTypeFunc, Index: 0},
73 FdWriteName: {Name: FdWriteName, Type: ExternTypeFunc, Index: 1},
74 },
75 NameSection: &NameSection{
76 ModuleName: InternalModuleName,
77 FunctionNames: NameMap{
78 {Index: 0, Name: ArgsSizesGetName},
79 {Index: 1, Name: FdWriteName},
80 },
81 LocalNames: IndirectNameMap{
82 {Index: 0, NameMap: NameMap{
83 {Index: 0, Name: "result.argc"},
84 {Index: 1, Name: "result.argv_len"},
85 }},
86 {Index: 1, NameMap: NameMap{
87 {Index: 0, Name: "fd"},
88 {Index: 1, Name: "iovs"},
89 {Index: 2, Name: "iovs_len"},
90 {Index: 3, Name: "result.size"},
91 }},
92 },
93 ResultNames: IndirectNameMap{
94 {Index: 0, NameMap: NameMap{{Index: 0, Name: "errno"}}},
95 {Index: 1, NameMap: NameMap{{Index: 0, Name: "errno"}}},
96 },
97 },
98 },
99 },
100 {
101 name: "multi-value",
102 moduleName: "swapper",
103 exportNames: []string{swapName},
104 nameToHostFunc: map[string]*HostFunc{swapName: {ExportName: swapName, Code: Code{GoFunc: swap}}},
105 expected: &Module{
106 TypeSection: []FunctionType{{Params: []ValueType{i32, i32}, Results: []ValueType{i32, i32}}},
107 FunctionSection: []Index{0},
108 CodeSection: []Code{MustParseGoReflectFuncCode(swap)},
109 ExportSection: []Export{{Name: "swap", Type: ExternTypeFunc, Index: 0}},
110 Exports: map[string]*Export{"swap": {Name: "swap", Type: ExternTypeFunc, Index: 0}},
111 NameSection: &NameSection{ModuleName: "swapper", FunctionNames: NameMap{{Index: 0, Name: "swap"}}},
112 },
113 },
114 }
115
116 for _, tt := range tests {
117 tc := tt
118
119 t.Run(tc.name, func(t *testing.T) {
120 m, e := NewHostModule(tc.moduleName, tc.exportNames, tc.nameToHostFunc, api.CoreFeaturesV2)
121 require.NoError(t, e)
122 requireHostModuleEquals(t, tc.expected, m)
123 require.True(t, m.IsHostModule)
124 })
125 }
126 }
127
128 func requireHostModuleEquals(t *testing.T, expected, actual *Module) {
129
130 require.Equal(t, expected.TypeSection, actual.TypeSection)
131 require.Equal(t, expected.ImportSection, actual.ImportSection)
132 require.Equal(t, expected.FunctionSection, actual.FunctionSection)
133 require.Equal(t, expected.TableSection, actual.TableSection)
134 require.Equal(t, expected.MemorySection, actual.MemorySection)
135 require.Equal(t, expected.GlobalSection, actual.GlobalSection)
136 require.Equal(t, expected.ExportSection, actual.ExportSection)
137 require.Equal(t, expected.StartSection, actual.StartSection)
138 require.Equal(t, expected.ElementSection, actual.ElementSection)
139 require.Equal(t, expected.DataSection, actual.DataSection)
140 require.Equal(t, expected.NameSection, actual.NameSection)
141
142
143
144 require.Equal(t, len(expected.CodeSection), len(actual.CodeSection))
145 for i, c := range expected.CodeSection {
146 actualCode := actual.CodeSection[i]
147 require.Equal(t, c.GoFunc, actualCode.GoFunc)
148
149
150 require.Nil(t, actualCode.Body)
151 require.Nil(t, actualCode.LocalTypes)
152 }
153 }
154
155 func TestNewHostModule_Errors(t *testing.T) {
156 tests := []struct {
157 name, moduleName string
158 exportNames []string
159 nameToHostFunc map[string]*HostFunc
160 expectedErr string
161 }{
162 {
163 name: "not a function",
164 moduleName: "modname",
165 exportNames: []string{"fn"},
166 nameToHostFunc: map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: t}}},
167 expectedErr: "func[modname.fn] kind != func: ptr",
168 },
169 {
170 name: "function has multiple results",
171 moduleName: "yetanother",
172 exportNames: []string{"fn"},
173 nameToHostFunc: map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: func() (uint32, uint32) { return 0, 0 }}}},
174 expectedErr: "func[yetanother.fn] multiple result types invalid as feature \"multi-value\" is disabled",
175 },
176 }
177
178 for _, tt := range tests {
179 tc := tt
180
181 t.Run(tc.name, func(t *testing.T) {
182 _, e := NewHostModule(tc.moduleName, tc.exportNames, tc.nameToHostFunc, api.CoreFeaturesV1)
183 require.EqualError(t, e, tc.expectedErr)
184 })
185 }
186 }
187
View as plain text