1 package binary
2
3 import (
4 "testing"
5
6 "github.com/tetratelabs/wazero/api"
7 "github.com/tetratelabs/wazero/internal/testing/binaryencoding"
8 "github.com/tetratelabs/wazero/internal/testing/dwarftestdata"
9 "github.com/tetratelabs/wazero/internal/testing/require"
10 "github.com/tetratelabs/wazero/internal/wasm"
11 )
12
13
14
15 func TestDecodeModule(t *testing.T) {
16 i32, f32 := wasm.ValueTypeI32, wasm.ValueTypeF32
17 zero := uint32(0)
18
19 tests := []struct {
20 name string
21 input *wasm.Module
22 }{
23 {
24 name: "empty",
25 input: &wasm.Module{},
26 },
27 {
28 name: "only name section",
29 input: &wasm.Module{NameSection: &wasm.NameSection{ModuleName: "simple"}},
30 },
31 {
32 name: "type section",
33 input: &wasm.Module{
34 TypeSection: []wasm.FunctionType{
35 {},
36 {Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
37 {Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}},
38 },
39 },
40 },
41 {
42 name: "type and import section",
43 input: &wasm.Module{
44 ImportFunctionCount: 2,
45 ImportTableCount: 1,
46 ImportMemoryCount: 1,
47 ImportGlobalCount: 3,
48 TypeSection: []wasm.FunctionType{
49 {Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
50 {Params: []wasm.ValueType{f32, f32}, Results: []wasm.ValueType{f32}},
51 },
52 ImportSection: []wasm.Import{
53 {
54 Module: "Math", Name: "Mul",
55 Type: wasm.ExternTypeFunc,
56 DescFunc: 1,
57 IndexPerType: 0,
58 },
59 {
60 Module: "foo", Name: "bar",
61 Type: wasm.ExternTypeTable,
62 DescTable: wasm.Table{Type: wasm.ValueTypeFuncref},
63 IndexPerType: 0,
64 },
65 {
66 Module: "Math", Name: "Add",
67 Type: wasm.ExternTypeFunc,
68 DescFunc: 0,
69 IndexPerType: 1,
70 },
71 {
72 Module: "bar", Name: "mem",
73 Type: wasm.ExternTypeMemory,
74 DescMem: &wasm.Memory{IsMaxEncoded: true},
75 IndexPerType: 0,
76 },
77 {
78 Module: "foo", Name: "bar2",
79 Type: wasm.ExternTypeGlobal,
80 DescGlobal: wasm.GlobalType{ValType: wasm.ValueTypeI32},
81 IndexPerType: 0,
82 },
83 {
84 Module: "foo", Name: "bar3",
85 Type: wasm.ExternTypeGlobal,
86 DescGlobal: wasm.GlobalType{ValType: wasm.ValueTypeI32},
87 IndexPerType: 1,
88 },
89 {
90 Module: "foo", Name: "bar4",
91 Type: wasm.ExternTypeGlobal,
92 DescGlobal: wasm.GlobalType{ValType: wasm.ValueTypeI32},
93 IndexPerType: 2,
94 },
95 },
96 },
97 },
98 {
99 name: "table and memory section",
100 input: &wasm.Module{
101 TableSection: []wasm.Table{{Min: 3, Type: wasm.RefTypeFuncref}},
102 MemorySection: &wasm.Memory{Min: 1, Cap: 1, Max: 1, IsMaxEncoded: true},
103 },
104 },
105 {
106 name: "type function and start section",
107 input: &wasm.Module{
108 ImportFunctionCount: 1,
109 TypeSection: []wasm.FunctionType{{}},
110 ImportSection: []wasm.Import{{
111 Module: "", Name: "hello",
112 Type: wasm.ExternTypeFunc,
113 DescFunc: 0,
114 }},
115 StartSection: &zero,
116 },
117 },
118 }
119
120 for _, tt := range tests {
121 tc := tt
122
123 t.Run(tc.name, func(t *testing.T) {
124 m, e := DecodeModule(binaryencoding.EncodeModule(tc.input), api.CoreFeaturesV1, wasm.MemoryLimitPages, false, false, false)
125 require.NoError(t, e)
126
127 for i := range tc.input.TypeSection {
128 tp := &(tc.input.TypeSection)[i]
129 _ = tp.String()
130 }
131 if len(tc.input.ImportSection) > 0 {
132 expImportPerModule := make(map[string][]*wasm.Import)
133 for i := range m.ImportSection {
134 imp := &m.ImportSection[i]
135 expImportPerModule[imp.Module] = append(expImportPerModule[imp.Module], imp)
136 }
137 tc.input.ImportPerModule = expImportPerModule
138 }
139 require.Equal(t, tc.input, m)
140 })
141 }
142
143 t.Run("skips custom section", func(t *testing.T) {
144 input := append(append(Magic, version...),
145 wasm.SectionIDCustom, 0xf,
146 0x04, 'm', 'e', 'm', 'e',
147 1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
148 m, e := DecodeModule(input, api.CoreFeaturesV1, wasm.MemoryLimitPages, false, false, false)
149 require.NoError(t, e)
150 require.Equal(t, &wasm.Module{}, m)
151 })
152
153 t.Run("reads custom sections", func(t *testing.T) {
154 input := append(append(Magic, version...),
155 wasm.SectionIDCustom, 0xf,
156 0x04, 'm', 'e', 'm', 'e',
157 1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
158 m, e := DecodeModule(input, api.CoreFeaturesV2, wasm.MemoryLimitPages, false, false, true)
159 require.NoError(t, e)
160 require.Equal(t, &wasm.Module{
161 CustomSections: []*wasm.CustomSection{
162 {
163 Name: "meme",
164 Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
165 },
166 },
167 }, m)
168 })
169
170 t.Run("skips custom section, but not name", func(t *testing.T) {
171 input := append(append(Magic, version...),
172 wasm.SectionIDCustom, 0xf,
173 0x04, 'm', 'e', 'm', 'e',
174 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
175 wasm.SectionIDCustom, 0x0e,
176 0x04, 'n', 'a', 'm', 'e',
177 subsectionIDModuleName, 0x07,
178 0x06,
179 's', 'i', 'm', 'p', 'l', 'e')
180 m, e := DecodeModule(input, api.CoreFeaturesV1, wasm.MemoryLimitPages, false, false, false)
181 require.NoError(t, e)
182 require.Equal(t, &wasm.Module{NameSection: &wasm.NameSection{ModuleName: "simple"}}, m)
183 })
184
185 t.Run("read custom sections and name separately", func(t *testing.T) {
186 input := append(append(Magic, version...),
187 wasm.SectionIDCustom, 0xf,
188 0x04, 'm', 'e', 'm', 'e',
189 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
190 wasm.SectionIDCustom, 0x0e,
191 0x04, 'n', 'a', 'm', 'e',
192 subsectionIDModuleName, 0x07,
193 0x06,
194 's', 'i', 'm', 'p', 'l', 'e')
195 m, e := DecodeModule(input, api.CoreFeaturesV2, wasm.MemoryLimitPages, false, false, true)
196 require.NoError(t, e)
197 require.Equal(t, &wasm.Module{
198 NameSection: &wasm.NameSection{ModuleName: "simple"},
199 CustomSections: []*wasm.CustomSection{
200 {
201 Name: "meme",
202 Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
203 },
204 },
205 }, m)
206 })
207
208 t.Run("DWARF enabled", func(t *testing.T) {
209 m, err := DecodeModule(dwarftestdata.ZigWasm, api.CoreFeaturesV2, wasm.MemoryLimitPages, false, true, true)
210 require.NoError(t, err)
211 require.NotNil(t, m.DWARFLines)
212 })
213
214 t.Run("DWARF disabled", func(t *testing.T) {
215 m, err := DecodeModule(dwarftestdata.ZigWasm, api.CoreFeaturesV2, wasm.MemoryLimitPages, false, false, true)
216 require.NoError(t, err)
217 require.Nil(t, m.DWARFLines)
218 })
219
220 t.Run("data count section disabled", func(t *testing.T) {
221 input := append(append(Magic, version...),
222 wasm.SectionIDDataCount, 1, 0)
223 _, e := DecodeModule(input, api.CoreFeaturesV1, wasm.MemoryLimitPages, false, false, false)
224 require.EqualError(t, e, `data count section not supported as feature "bulk-memory-operations" is disabled`)
225 })
226 }
227
228 func TestDecodeModule_Errors(t *testing.T) {
229 tests := []struct {
230 name string
231 input []byte
232 expectedErr string
233 }{
234 {
235 name: "wrong magic",
236 input: []byte("wasm\x01\x00\x00\x00"),
237 expectedErr: "invalid magic number",
238 },
239 {
240 name: "wrong version",
241 input: []byte("\x00asm\x01\x00\x00\x01"),
242 expectedErr: "invalid version header",
243 },
244 {
245 name: "multiple start sections",
246 input: append(append(Magic, version...),
247 wasm.SectionIDType, 4, 1, 0x60, 0, 0,
248 wasm.SectionIDFunction, 2, 1, 0,
249 wasm.SectionIDCode, 4, 1,
250 2, 0, wasm.OpcodeEnd,
251 wasm.SectionIDStart, 1, 0,
252 wasm.SectionIDStart, 1, 0,
253 ),
254 expectedErr: `multiple start sections are invalid`,
255 },
256 {
257 name: "redundant name section",
258 input: append(append(Magic, version...),
259 wasm.SectionIDCustom, 0x09,
260 0x04, 'n', 'a', 'm', 'e',
261 subsectionIDModuleName, 0x02, 0x01, 'x',
262 wasm.SectionIDCustom, 0x09,
263 0x04, 'n', 'a', 'm', 'e',
264 subsectionIDModuleName, 0x02, 0x01, 'x'),
265 expectedErr: "section custom: redundant custom section name",
266 },
267 }
268
269 for _, tt := range tests {
270 tc := tt
271
272 t.Run(tc.name, func(t *testing.T) {
273 _, e := DecodeModule(tc.input, api.CoreFeaturesV1, wasm.MemoryLimitPages, false, false, false)
274 require.EqualError(t, e, tc.expectedErr)
275 })
276 }
277 }
278
View as plain text