1 package wasm
2
3 import (
4 "context"
5 "fmt"
6 "math"
7 "testing"
8
9 "github.com/tetratelabs/wazero/api"
10 "github.com/tetratelabs/wazero/experimental"
11 "github.com/tetratelabs/wazero/internal/leb128"
12 "github.com/tetratelabs/wazero/internal/testing/require"
13 "github.com/tetratelabs/wazero/internal/u64"
14 )
15
16 func TestFunctionType_String(t *testing.T) {
17 tests := []struct {
18 functype *FunctionType
19 exp string
20 }{
21 {functype: &FunctionType{}, exp: "v_v"},
22 {functype: &FunctionType{Params: []ValueType{ValueTypeI32}}, exp: "i32_v"},
23 {functype: &FunctionType{Params: []ValueType{ValueTypeI32, ValueTypeF64}}, exp: "i32f64_v"},
24 {functype: &FunctionType{Params: []ValueType{ValueTypeF32, ValueTypeI32, ValueTypeF64}}, exp: "f32i32f64_v"},
25 {functype: &FunctionType{Results: []ValueType{ValueTypeI64}}, exp: "v_i64"},
26 {functype: &FunctionType{Results: []ValueType{ValueTypeI64, ValueTypeF32}}, exp: "v_i64f32"},
27 {functype: &FunctionType{Results: []ValueType{ValueTypeF32, ValueTypeI32, ValueTypeF64}}, exp: "v_f32i32f64"},
28 {functype: &FunctionType{Params: []ValueType{ValueTypeI32}, Results: []ValueType{ValueTypeI64}}, exp: "i32_i64"},
29 {functype: &FunctionType{Params: []ValueType{ValueTypeI64, ValueTypeF32}, Results: []ValueType{ValueTypeI64, ValueTypeF32}}, exp: "i64f32_i64f32"},
30 {functype: &FunctionType{Params: []ValueType{ValueTypeI64, ValueTypeF32, ValueTypeF64}, Results: []ValueType{ValueTypeF32, ValueTypeI32, ValueTypeF64}}, exp: "i64f32f64_f32i32f64"},
31 }
32
33 for _, tt := range tests {
34 tc := tt
35 t.Run(tc.functype.String(), func(t *testing.T) {
36 require.Equal(t, tc.exp, tc.functype.String())
37 require.Equal(t, tc.exp, tc.functype.key())
38 require.Equal(t, tc.exp, tc.functype.string)
39 })
40 }
41 }
42
43 func TestSectionIDName(t *testing.T) {
44 tests := []struct {
45 name string
46 input SectionID
47 expected string
48 }{
49 {"custom", SectionIDCustom, "custom"},
50 {"type", SectionIDType, "type"},
51 {"import", SectionIDImport, "import"},
52 {"function", SectionIDFunction, "function"},
53 {"table", SectionIDTable, "table"},
54 {"memory", SectionIDMemory, "memory"},
55 {"global", SectionIDGlobal, "global"},
56 {"export", SectionIDExport, "export"},
57 {"start", SectionIDStart, "start"},
58 {"element", SectionIDElement, "element"},
59 {"code", SectionIDCode, "code"},
60 {"data", SectionIDData, "data"},
61 {"unknown", 100, "unknown"},
62 }
63
64 for _, tt := range tests {
65 tc := tt
66
67 t.Run(tc.name, func(t *testing.T) {
68 require.Equal(t, tc.expected, SectionIDName(tc.input))
69 })
70 }
71 }
72
73 func TestMemory_Validate(t *testing.T) {
74 tests := []struct {
75 name string
76 mem *Memory
77 expectedErr string
78 }{
79 {
80 name: "ok",
81 mem: &Memory{Min: 2, Cap: 2, Max: 2},
82 },
83 {
84 name: "cap < min",
85 mem: &Memory{Min: 2, Cap: 1, Max: 2},
86 expectedErr: "capacity 1 pages (64 Ki) less than minimum 2 pages (128 Ki)",
87 },
88 {
89 name: "cap > maxLimit",
90 mem: &Memory{Min: 2, Cap: math.MaxUint32, Max: 2},
91 expectedErr: "capacity 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)",
92 },
93 {
94 name: "max < min",
95 mem: &Memory{Min: 2, Cap: 2, Max: 0, IsMaxEncoded: true},
96 expectedErr: "min 2 pages (128 Ki) > max 0 pages (0 Ki)",
97 },
98 {
99 name: "min > limit",
100 mem: &Memory{Min: math.MaxUint32},
101 expectedErr: "min 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)",
102 },
103 {
104 name: "max > limit",
105 mem: &Memory{Max: math.MaxUint32, IsMaxEncoded: true},
106 expectedErr: "max 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)",
107 },
108 }
109
110 for _, tt := range tests {
111 tc := tt
112
113 t.Run(tc.name, func(t *testing.T) {
114 err := tc.mem.Validate(MemoryLimitPages)
115 if tc.expectedErr == "" {
116 require.NoError(t, err)
117 } else {
118 require.EqualError(t, err, tc.expectedErr)
119 }
120 })
121 }
122 }
123
124 func TestModule_allDeclarations(t *testing.T) {
125 tests := []struct {
126 module *Module
127 expectedFunctions []Index
128 expectedGlobals []GlobalType
129 expectedMemory *Memory
130 expectedTables []Table
131 }{
132
133 {
134 module: &Module{
135 ImportSection: []Import{{Type: ExternTypeFunc, DescFunc: 10000}},
136 FunctionSection: []Index{10, 20, 30},
137 },
138 expectedFunctions: []Index{10000, 10, 20, 30},
139 },
140 {
141 module: &Module{
142 FunctionSection: []Index{10, 20, 30},
143 },
144 expectedFunctions: []Index{10, 20, 30},
145 },
146 {
147 module: &Module{
148 ImportSection: []Import{{Type: ExternTypeFunc, DescFunc: 10000}},
149 },
150 expectedFunctions: []Index{10000},
151 },
152
153 {
154 module: &Module{
155 ImportSection: []Import{{Type: ExternTypeGlobal, DescGlobal: GlobalType{Mutable: false}}},
156 GlobalSection: []Global{{Type: GlobalType{Mutable: true}}},
157 },
158 expectedGlobals: []GlobalType{{Mutable: false}, {Mutable: true}},
159 },
160 {
161 module: &Module{
162 GlobalSection: []Global{{Type: GlobalType{Mutable: true}}},
163 },
164 expectedGlobals: []GlobalType{{Mutable: true}},
165 },
166 {
167 module: &Module{
168 ImportSection: []Import{{Type: ExternTypeGlobal, DescGlobal: GlobalType{Mutable: false}}},
169 },
170 expectedGlobals: []GlobalType{{Mutable: false}},
171 },
172
173 {
174 module: &Module{
175 ImportSection: []Import{{Type: ExternTypeMemory, DescMem: &Memory{Min: 1, Max: 10}}},
176 },
177 expectedMemory: &Memory{Min: 1, Max: 10},
178 },
179 {
180 module: &Module{
181 MemorySection: &Memory{Min: 100},
182 },
183 expectedMemory: &Memory{Min: 100},
184 },
185
186 {
187 module: &Module{
188 ImportSection: []Import{{Type: ExternTypeTable, DescTable: Table{Min: 1}}},
189 },
190 expectedTables: []Table{{Min: 1}},
191 },
192 {
193 module: &Module{
194 TableSection: []Table{{Min: 10}},
195 },
196 expectedTables: []Table{{Min: 10}},
197 },
198 }
199
200 for i, tt := range tests {
201 tc := tt
202 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
203 functions, globals, memory, tables, err := tc.module.AllDeclarations()
204 require.NoError(t, err)
205 require.Equal(t, tc.expectedFunctions, functions)
206 require.Equal(t, tc.expectedGlobals, globals)
207 require.Equal(t, tc.expectedTables, tables)
208 require.Equal(t, tc.expectedMemory, memory)
209 })
210 }
211 }
212
213 func TestValidateConstExpression(t *testing.T) {
214 t.Run("invalid opcode", func(t *testing.T) {
215 expr := ConstantExpression{Opcode: OpcodeNop}
216 err := validateConstExpression(nil, 0, &expr, valueTypeUnknown)
217 require.Error(t, err)
218 })
219 for _, vt := range []ValueType{ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64} {
220 t.Run(ValueTypeName(vt), func(t *testing.T) {
221 t.Run("valid", func(t *testing.T) {
222 expr := ConstantExpression{}
223 switch vt {
224 case ValueTypeI32:
225 expr.Data = []byte{1}
226 expr.Opcode = OpcodeI32Const
227 case ValueTypeI64:
228 expr.Data = []byte{2}
229 expr.Opcode = OpcodeI64Const
230 case ValueTypeF32:
231 expr.Data = u64.LeBytes(api.EncodeF32(math.MaxFloat32))
232 expr.Opcode = OpcodeF32Const
233 case ValueTypeF64:
234 expr.Data = u64.LeBytes(api.EncodeF64(math.MaxFloat64))
235 expr.Opcode = OpcodeF64Const
236 }
237
238 err := validateConstExpression(nil, 0, &expr, vt)
239 require.NoError(t, err)
240 })
241 t.Run("invalid", func(t *testing.T) {
242
243 expr := ConstantExpression{Data: make([]byte, 0)}
244 switch vt {
245 case ValueTypeI32:
246 expr.Opcode = OpcodeI32Const
247 case ValueTypeI64:
248 expr.Opcode = OpcodeI64Const
249 case ValueTypeF32:
250 expr.Opcode = OpcodeF32Const
251 case ValueTypeF64:
252 expr.Opcode = OpcodeF64Const
253 }
254 err := validateConstExpression(nil, 0, &expr, vt)
255 require.Error(t, err)
256 })
257 })
258 }
259 t.Run("ref types", func(t *testing.T) {
260 t.Run("ref.func", func(t *testing.T) {
261 expr := &ConstantExpression{Data: []byte{5}, Opcode: OpcodeRefFunc}
262 err := validateConstExpression(nil, 10, expr, ValueTypeFuncref)
263 require.NoError(t, err)
264 err = validateConstExpression(nil, 2, expr, ValueTypeFuncref)
265 require.EqualError(t, err, "ref.func index out of range [5] with length 1")
266 })
267 t.Run("ref.null", func(t *testing.T) {
268 err := validateConstExpression(nil, 0,
269 &ConstantExpression{Data: []byte{ValueTypeFuncref}, Opcode: OpcodeRefNull},
270 ValueTypeFuncref)
271 require.NoError(t, err)
272 err = validateConstExpression(nil, 0,
273 &ConstantExpression{Data: []byte{ValueTypeExternref}, Opcode: OpcodeRefNull},
274 ValueTypeExternref)
275 require.NoError(t, err)
276 err = validateConstExpression(nil, 0,
277 &ConstantExpression{Data: []byte{0xff}, Opcode: OpcodeRefNull},
278 ValueTypeExternref)
279 require.EqualError(t, err, "invalid type for ref.null: 0xff")
280 })
281 })
282 t.Run("global expr", func(t *testing.T) {
283 t.Run("failed to read global index", func(t *testing.T) {
284
285 expr := &ConstantExpression{Data: make([]byte, 0), Opcode: OpcodeGlobalGet}
286 err := validateConstExpression(nil, 0, expr, valueTypeUnknown)
287 require.Error(t, err)
288 })
289 t.Run("global index out of range", func(t *testing.T) {
290
291 expr := &ConstantExpression{Data: []byte{1}, Opcode: OpcodeGlobalGet}
292 var globals []GlobalType
293 err := validateConstExpression(globals, 0, expr, valueTypeUnknown)
294 require.Error(t, err)
295 })
296
297 t.Run("type mismatch", func(t *testing.T) {
298 for _, vt := range []ValueType{
299 ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64,
300 } {
301 t.Run(ValueTypeName(vt), func(t *testing.T) {
302
303 expr := &ConstantExpression{Data: []byte{0}, Opcode: OpcodeGlobalGet}
304 globals := []GlobalType{{ValType: valueTypeUnknown}}
305
306 err := validateConstExpression(globals, 0, expr, vt)
307 require.Error(t, err)
308 })
309 }
310 })
311 t.Run("ok", func(t *testing.T) {
312 for _, vt := range []ValueType{
313 ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64,
314 } {
315 t.Run(ValueTypeName(vt), func(t *testing.T) {
316
317 expr := &ConstantExpression{Data: []byte{0}, Opcode: OpcodeGlobalGet}
318 globals := []GlobalType{{ValType: vt}}
319
320 err := validateConstExpression(globals, 0, expr, vt)
321 require.NoError(t, err)
322 })
323 }
324 })
325 })
326 }
327
328 func TestModule_Validate_Errors(t *testing.T) {
329 zero := Index(0)
330 tests := []struct {
331 name string
332 input *Module
333 expectedErr string
334 }{
335 {
336 name: "StartSection points to an invalid func",
337 input: &Module{
338 TypeSection: nil,
339 FunctionSection: []uint32{0},
340 CodeSection: []Code{{Body: []byte{OpcodeEnd}}},
341 StartSection: &zero,
342 },
343 expectedErr: "invalid start function: func[0] has an invalid type",
344 },
345 }
346
347 for _, tt := range tests {
348 tc := tt
349
350 t.Run(tc.name, func(t *testing.T) {
351 err := tc.input.Validate(api.CoreFeaturesV1)
352 require.EqualError(t, err, tc.expectedErr)
353 })
354 }
355 }
356
357 func TestModule_validateStartSection(t *testing.T) {
358 t.Run("no start section", func(t *testing.T) {
359 m := Module{}
360 err := m.validateStartSection()
361 require.NoError(t, err)
362 })
363
364 t.Run("invalid type", func(t *testing.T) {
365 for _, ft := range []FunctionType{
366 {Params: []ValueType{ValueTypeI32}},
367 {Results: []ValueType{ValueTypeI32}},
368 {Params: []ValueType{ValueTypeI32}, Results: []ValueType{ValueTypeI32}},
369 } {
370 t.Run(ft.String(), func(t *testing.T) {
371 index := uint32(0)
372 m := Module{StartSection: &index, FunctionSection: []uint32{0}, TypeSection: []FunctionType{ft}}
373 err := m.validateStartSection()
374 require.Error(t, err)
375 })
376 }
377 })
378 t.Run("imported valid func", func(t *testing.T) {
379 index := Index(1)
380 m := Module{
381 StartSection: &index,
382 TypeSection: []FunctionType{{}, {Results: []ValueType{ValueTypeI32}}},
383 ImportFunctionCount: 2,
384 ImportSection: []Import{
385 {Type: ExternTypeFunc, DescFunc: 1},
386
387 {Type: ExternTypeGlobal},
388 {Type: ExternTypeFunc, DescFunc: 0},
389 },
390 }
391 err := m.validateStartSection()
392 require.NoError(t, err)
393 })
394 }
395
396 func TestModule_validateGlobals(t *testing.T) {
397 t.Run("too many globals", func(t *testing.T) {
398 m := Module{}
399 err := m.validateGlobals(make([]GlobalType, 10), 0, 9)
400 require.Error(t, err)
401 require.EqualError(t, err, "too many globals in a module")
402 })
403 t.Run("global index out of range", func(t *testing.T) {
404 m := Module{GlobalSection: []Global{
405 {
406 Type: GlobalType{ValType: ValueTypeI32},
407
408 Init: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{1}},
409 },
410 }}
411 err := m.validateGlobals(nil, 0, 9)
412 require.Error(t, err)
413 require.EqualError(t, err, "global index out of range")
414 })
415 t.Run("invalid const expression", func(t *testing.T) {
416 m := Module{GlobalSection: []Global{
417 {
418 Type: GlobalType{ValType: valueTypeUnknown},
419 Init: ConstantExpression{Opcode: OpcodeUnreachable},
420 },
421 }}
422 err := m.validateGlobals(nil, 0, 9)
423 require.Error(t, err)
424 require.EqualError(t, err, "invalid opcode for const expression: 0x0")
425 })
426 t.Run("ok", func(t *testing.T) {
427 m := Module{GlobalSection: []Global{
428 {
429 Type: GlobalType{ValType: ValueTypeI32},
430 Init: ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
431 },
432 }}
433 err := m.validateGlobals(nil, 0, 9)
434 require.NoError(t, err)
435 })
436 t.Run("ok with imported global", func(t *testing.T) {
437 m := Module{
438 ImportGlobalCount: 1,
439 GlobalSection: []Global{
440 {
441 Type: GlobalType{ValType: ValueTypeI32},
442
443 Init: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0}},
444 },
445 },
446 ImportSection: []Import{{Type: ExternTypeGlobal}},
447 }
448 globalDeclarations := []GlobalType{
449 {ValType: ValueTypeI32},
450 {},
451 }
452 err := m.validateGlobals(globalDeclarations, 0, 9)
453 require.NoError(t, err)
454 })
455 }
456
457 func TestModule_validateFunctions(t *testing.T) {
458 t.Run("ok", func(t *testing.T) {
459 m := Module{
460 TypeSection: []FunctionType{v_v},
461 FunctionSection: []uint32{0},
462 CodeSection: []Code{{Body: []byte{OpcodeI32Const, 0, OpcodeDrop, OpcodeEnd}}},
463 }
464 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
465 require.NoError(t, err)
466 })
467 t.Run("too many functions", func(t *testing.T) {
468 m := Module{}
469 err := m.validateFunctions(api.CoreFeaturesV1, []uint32{1, 2, 3, 4}, nil, nil, nil, 3)
470 require.Error(t, err)
471 require.EqualError(t, err, "too many functions (4) in a module")
472 })
473 t.Run("function, but no code", func(t *testing.T) {
474 m := Module{
475 TypeSection: []FunctionType{v_v},
476 FunctionSection: []Index{0},
477 CodeSection: nil,
478 }
479 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
480 require.Error(t, err)
481 require.EqualError(t, err, "code count (0) != function count (1)")
482 })
483 t.Run("function out of range of code", func(t *testing.T) {
484 m := Module{
485 TypeSection: []FunctionType{v_v},
486 FunctionSection: []Index{1},
487 CodeSection: []Code{{Body: []byte{OpcodeEnd}}},
488 }
489 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
490 require.Error(t, err)
491 require.EqualError(t, err, "invalid function[0]: type section index 1 out of range")
492 })
493 t.Run("invalid", func(t *testing.T) {
494 m := Module{
495 TypeSection: []FunctionType{v_v},
496 FunctionSection: []Index{0},
497 CodeSection: []Code{{Body: []byte{OpcodeF32Abs}}},
498 }
499 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
500 require.Error(t, err)
501 require.Contains(t, err.Error(), "invalid function[0]: cannot pop the 1st f32 operand")
502 })
503 t.Run("in- exported", func(t *testing.T) {
504 m := Module{
505 TypeSection: []FunctionType{v_v},
506 FunctionSection: []Index{0},
507 CodeSection: []Code{{Body: []byte{OpcodeF32Abs}}},
508 ExportSection: []Export{{Name: "f1", Type: ExternTypeFunc, Index: 0}},
509 }
510 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
511 require.Error(t, err)
512 require.Contains(t, err.Error(), `invalid function[0] export["f1"]: cannot pop the 1st f32`)
513 })
514 t.Run("in- exported after import", func(t *testing.T) {
515 m := Module{
516 ImportFunctionCount: 1,
517 TypeSection: []FunctionType{v_v},
518 ImportSection: []Import{{Type: ExternTypeFunc}},
519 FunctionSection: []Index{0},
520 CodeSection: []Code{{Body: []byte{OpcodeF32Abs}}},
521 ExportSection: []Export{{Name: "f1", Type: ExternTypeFunc, Index: 1}},
522 }
523 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
524 require.Error(t, err)
525 require.Contains(t, err.Error(), `invalid function[0] export["f1"]: cannot pop the 1st f32`)
526 })
527 t.Run("in- exported twice", func(t *testing.T) {
528 m := Module{
529 TypeSection: []FunctionType{v_v},
530 FunctionSection: []Index{0},
531 CodeSection: []Code{{Body: []byte{OpcodeF32Abs}}},
532 ExportSection: []Export{
533 {Name: "f1", Type: ExternTypeFunc, Index: 0},
534 {Name: "f2", Type: ExternTypeFunc, Index: 0},
535 },
536 }
537 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
538 require.Error(t, err)
539 require.Contains(t, err.Error(), `invalid function[0] export["f1","f2"]: cannot pop the 1st f32`)
540 })
541 }
542
543 func TestModule_validateMemory(t *testing.T) {
544 t.Run("active data segment exits but memory not declared", func(t *testing.T) {
545 m := Module{DataSection: []DataSegment{{OffsetExpression: ConstantExpression{}}}}
546 err := m.validateMemory(nil, nil, api.CoreFeaturesV1)
547 require.Error(t, err)
548 require.Contains(t, "unknown memory", err.Error())
549 })
550 t.Run("invalid const expr", func(t *testing.T) {
551 m := Module{DataSection: []DataSegment{{
552 OffsetExpression: ConstantExpression{
553 Opcode: OpcodeUnreachable,
554 },
555 }}}
556 err := m.validateMemory(&Memory{}, nil, api.CoreFeaturesV1)
557 require.EqualError(t, err, "calculate offset: invalid opcode for const expression: 0x0")
558 })
559 t.Run("ok", func(t *testing.T) {
560 m := Module{DataSection: []DataSegment{{
561 Init: []byte{0x1},
562 OffsetExpression: ConstantExpression{
563 Opcode: OpcodeI32Const,
564 Data: leb128.EncodeInt32(1),
565 },
566 }}}
567 err := m.validateMemory(&Memory{}, nil, api.CoreFeaturesV1)
568 require.NoError(t, err)
569 })
570 }
571
572 func TestModule_validateImports(t *testing.T) {
573 tests := []struct {
574 name string
575 enabledFeatures api.CoreFeatures
576 i *Import
577 expectedErr string
578 }{
579 {name: "empty import section"},
580 {
581 name: "reject empty named module",
582 enabledFeatures: api.CoreFeaturesV1,
583 i: &Import{Module: "", Name: "n", Type: ExternTypeFunc, DescFunc: 0},
584 expectedErr: "import[0] has an empty module name",
585 },
586 {
587 name: "func",
588 enabledFeatures: api.CoreFeaturesV1,
589 i: &Import{Module: "m", Name: "n", Type: ExternTypeFunc, DescFunc: 0},
590 },
591 {
592 name: "func type index out of range ",
593 enabledFeatures: api.CoreFeaturesV1,
594 i: &Import{Module: "m", Name: "n", Type: ExternTypeFunc, DescFunc: 100},
595 expectedErr: "invalid import[\"m\".\"n\"] function: type index out of range",
596 },
597 {
598 name: "global var disabled",
599 enabledFeatures: api.CoreFeaturesV1.SetEnabled(api.CoreFeatureMutableGlobal, false),
600 i: &Import{
601 Module: "m",
602 Name: "n",
603 Type: ExternTypeGlobal,
604 DescGlobal: GlobalType{ValType: ValueTypeI32, Mutable: true},
605 },
606 expectedErr: `invalid import["m"."n"] global: feature "mutable-global" is disabled`,
607 },
608 {
609 name: "table",
610 enabledFeatures: api.CoreFeaturesV1,
611 i: &Import{
612 Module: "m",
613 Name: "n",
614 Type: ExternTypeTable,
615 DescTable: Table{Min: 1},
616 },
617 },
618 {
619 name: "memory",
620 enabledFeatures: api.CoreFeaturesV1,
621 i: &Import{
622 Module: "m",
623 Name: "n",
624 Type: ExternTypeMemory,
625 DescMem: &Memory{Min: 1},
626 },
627 },
628 }
629
630 for _, tt := range tests {
631 tc := tt
632 t.Run(tc.name, func(t *testing.T) {
633 m := Module{TypeSection: []FunctionType{{}}}
634 if tc.i != nil {
635 m.ImportSection = []Import{*tc.i}
636 }
637 err := m.validateImports(tc.enabledFeatures)
638 if tc.expectedErr != "" {
639 require.EqualError(t, err, tc.expectedErr)
640 } else {
641 require.NoError(t, err)
642 }
643 })
644 }
645 }
646
647 func TestModule_validateExports(t *testing.T) {
648 tests := []struct {
649 name string
650 enabledFeatures api.CoreFeatures
651 exportSection []Export
652 functions []Index
653 globals []GlobalType
654 memory *Memory
655 tables []Table
656 expectedErr string
657 }{
658 {name: "empty export section", exportSection: []Export{}},
659 {
660 name: "func",
661 enabledFeatures: api.CoreFeaturesV1,
662 exportSection: []Export{{Type: ExternTypeFunc, Index: 0}},
663 functions: []Index{100 },
664 },
665 {
666 name: "func out of range",
667 enabledFeatures: api.CoreFeaturesV1,
668 exportSection: []Export{{Type: ExternTypeFunc, Index: 1, Name: "e"}},
669 functions: []Index{100 },
670 expectedErr: `unknown function for export["e"]`,
671 },
672 {
673 name: "global const",
674 enabledFeatures: api.CoreFeaturesV1,
675 exportSection: []Export{{Type: ExternTypeGlobal, Index: 0}},
676 globals: []GlobalType{{ValType: ValueTypeI32}},
677 },
678 {
679 name: "global var",
680 enabledFeatures: api.CoreFeaturesV1,
681 exportSection: []Export{{Type: ExternTypeGlobal, Index: 0}},
682 globals: []GlobalType{{ValType: ValueTypeI32, Mutable: true}},
683 },
684 {
685 name: "global var disabled",
686 enabledFeatures: api.CoreFeaturesV1.SetEnabled(api.CoreFeatureMutableGlobal, false),
687 exportSection: []Export{{Type: ExternTypeGlobal, Index: 0, Name: "e"}},
688 globals: []GlobalType{{ValType: ValueTypeI32, Mutable: true}},
689 expectedErr: `invalid export["e"] global[0]: feature "mutable-global" is disabled`,
690 },
691 {
692 name: "global out of range",
693 enabledFeatures: api.CoreFeaturesV1,
694 exportSection: []Export{{Type: ExternTypeGlobal, Index: 1, Name: "e"}},
695 globals: []GlobalType{{}},
696 expectedErr: `unknown global for export["e"]`,
697 },
698 {
699 name: "table",
700 enabledFeatures: api.CoreFeaturesV1,
701 exportSection: []Export{{Type: ExternTypeTable, Index: 0}},
702 tables: []Table{{}},
703 },
704 {
705 name: "multiple tables",
706 enabledFeatures: api.CoreFeaturesV1,
707 exportSection: []Export{{Type: ExternTypeTable, Index: 0}, {Type: ExternTypeTable, Index: 1}, {Type: ExternTypeTable, Index: 2}},
708 tables: []Table{{}, {}, {}},
709 },
710 {
711 name: "table out of range",
712 enabledFeatures: api.CoreFeaturesV1,
713 exportSection: []Export{{Type: ExternTypeTable, Index: 1, Name: "e"}},
714 tables: []Table{},
715 expectedErr: `table for export["e"] out of range`,
716 },
717 {
718 name: "memory",
719 enabledFeatures: api.CoreFeaturesV1,
720 exportSection: []Export{{Type: ExternTypeMemory, Index: 0}},
721 memory: &Memory{},
722 },
723 {
724 name: "memory out of range",
725 enabledFeatures: api.CoreFeaturesV1,
726 exportSection: []Export{{Type: ExternTypeMemory, Index: 0, Name: "e"}},
727 tables: []Table{},
728 expectedErr: `memory for export["e"] out of range`,
729 },
730 }
731
732 for _, tt := range tests {
733 tc := tt
734 t.Run(tc.name, func(t *testing.T) {
735 m := Module{ExportSection: tc.exportSection}
736 err := m.validateExports(tc.enabledFeatures, tc.functions, tc.globals, tc.memory, tc.tables)
737 if tc.expectedErr != "" {
738 require.EqualError(t, err, tc.expectedErr)
739 } else {
740 require.NoError(t, err)
741 }
742 })
743 }
744 }
745
746 func TestModule_buildGlobals(t *testing.T) {
747 const localFuncRefInstructionIndex = uint32(0xffff)
748
749 minusOne := int32(-1)
750 m := &Module{
751 ImportGlobalCount: 2,
752 GlobalSection: []Global{
753 {
754 Type: GlobalType{Mutable: true, ValType: ValueTypeF64},
755 Init: ConstantExpression{
756 Opcode: OpcodeF64Const,
757 Data: u64.LeBytes(api.EncodeF64(math.MaxFloat64)),
758 },
759 },
760 {
761 Type: GlobalType{Mutable: false, ValType: ValueTypeI32},
762 Init: ConstantExpression{
763 Opcode: OpcodeI32Const,
764 Data: leb128.EncodeInt32(math.MaxInt32),
765 },
766 },
767 {
768 Type: GlobalType{Mutable: false, ValType: ValueTypeI32},
769 Init: ConstantExpression{
770 Opcode: OpcodeI32Const,
771 Data: leb128.EncodeInt32(minusOne),
772 },
773 },
774 {
775 Type: GlobalType{Mutable: false, ValType: ValueTypeV128},
776 Init: ConstantExpression{
777 Opcode: OpcodeVecV128Const,
778 Data: []byte{
779 1, 0, 0, 0, 0, 0, 0, 0,
780 2, 0, 0, 0, 0, 0, 0, 0,
781 },
782 },
783 },
784 {
785 Type: GlobalType{Mutable: false, ValType: ValueTypeExternref},
786 Init: ConstantExpression{Opcode: OpcodeRefNull, Data: []byte{ValueTypeExternref}},
787 },
788 {
789 Type: GlobalType{Mutable: false, ValType: ValueTypeFuncref},
790 Init: ConstantExpression{Opcode: OpcodeRefNull, Data: []byte{ValueTypeFuncref}},
791 },
792 {
793 Type: GlobalType{Mutable: false, ValType: ValueTypeFuncref},
794 Init: ConstantExpression{Opcode: OpcodeRefFunc, Data: leb128.EncodeUint32(localFuncRefInstructionIndex)},
795 },
796 {
797 Type: GlobalType{Mutable: false, ValType: ValueTypeExternref},
798 Init: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0}},
799 },
800 {
801 Type: GlobalType{Mutable: false, ValType: ValueTypeFuncref},
802 Init: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{1}},
803 },
804 },
805 }
806
807 imported := []*GlobalInstance{
808 {Type: GlobalType{ValType: ValueTypeExternref}, Val: 0x54321},
809 {Type: GlobalType{ValType: ValueTypeFuncref}, Val: 0x12345},
810 }
811
812 mi := &ModuleInstance{
813 Globals: make([]*GlobalInstance, m.ImportGlobalCount+uint32(len(m.GlobalSection))),
814 Engine: &mockModuleEngine{},
815 }
816
817 mi.Globals[0], mi.Globals[1] = imported[0], imported[1]
818
819 mi.buildGlobals(m, func(funcIndex Index) Reference {
820 require.Equal(t, localFuncRefInstructionIndex, funcIndex)
821 return 0x99999
822 })
823 expectedGlobals := []*GlobalInstance{
824 imported[0], imported[1],
825 {Type: GlobalType{ValType: ValueTypeF64, Mutable: true}, Val: api.EncodeF64(math.MaxFloat64)},
826 {Type: GlobalType{ValType: ValueTypeI32, Mutable: false}, Val: uint64(int32(math.MaxInt32))},
827
828 {Type: GlobalType{ValType: ValueTypeI32, Mutable: false}, Val: uint64(uint32(minusOne))},
829 {Type: GlobalType{ValType: ValueTypeV128, Mutable: false}, Val: 0x1, ValHi: 0x2},
830 {Type: GlobalType{ValType: ValueTypeExternref, Mutable: false}, Val: 0},
831 {Type: GlobalType{ValType: ValueTypeFuncref, Mutable: false}, Val: 0},
832 {Type: GlobalType{ValType: ValueTypeFuncref, Mutable: false}, Val: 0x99999},
833 {Type: GlobalType{ValType: ValueTypeExternref, Mutable: false}, Val: 0x54321},
834 {Type: GlobalType{ValType: ValueTypeFuncref, Mutable: false}, Val: 0x12345},
835 }
836 require.Equal(t, expectedGlobals, mi.Globals)
837 }
838
839 func TestModule_buildMemoryInstance(t *testing.T) {
840 t.Run("nil", func(t *testing.T) {
841 m := ModuleInstance{}
842 m.buildMemory(&Module{})
843 require.Nil(t, m.MemoryInstance)
844 })
845 t.Run("non-nil", func(t *testing.T) {
846 min := uint32(1)
847 max := uint32(10)
848 mDef := MemoryDefinition{moduleName: "foo"}
849 m := ModuleInstance{}
850 m.buildMemory(&Module{
851 MemorySection: &Memory{Min: min, Cap: min, Max: max},
852 MemoryDefinitionSection: []MemoryDefinition{mDef},
853 })
854 mem := m.MemoryInstance
855 require.Equal(t, min, mem.Min)
856 require.Equal(t, max, mem.Max)
857 require.Equal(t, &mDef, mem.definition)
858 })
859 }
860
861 func TestModule_validateDataCountSection(t *testing.T) {
862 t.Run("ok", func(t *testing.T) {
863 for _, m := range []*Module{
864 {
865 DataSection: []DataSegment{},
866 DataCountSection: nil,
867 },
868 {
869 DataSection: []DataSegment{{}, {}},
870 DataCountSection: nil,
871 },
872 } {
873 err := m.validateDataCountSection()
874 require.NoError(t, err)
875 }
876 })
877 t.Run("error", func(t *testing.T) {
878 count := uint32(1)
879 for _, m := range []*Module{
880 {
881 DataSection: []DataSegment{},
882 DataCountSection: &count,
883 },
884 {
885 DataSection: []DataSegment{{}, {}},
886 DataCountSection: &count,
887 },
888 } {
889 err := m.validateDataCountSection()
890 require.Error(t, err)
891 }
892 })
893 }
894
895 func TestModule_declaredFunctionIndexes(t *testing.T) {
896 tests := []struct {
897 name string
898 mod *Module
899 exp map[Index]struct{}
900 expErr string
901 }{
902 {
903 name: "empty",
904 mod: &Module{},
905 exp: map[uint32]struct{}{},
906 },
907 {
908 name: "global",
909 mod: &Module{
910 ExportSection: []Export{
911 {Index: 10, Type: ExternTypeFunc},
912 {Index: 1000, Type: ExternTypeGlobal},
913 },
914 },
915 exp: map[uint32]struct{}{10: {}},
916 },
917 {
918 name: "export",
919 mod: &Module{
920 ExportSection: []Export{
921 {Index: 1000, Type: ExternTypeGlobal},
922 {Index: 10, Type: ExternTypeFunc},
923 },
924 },
925 exp: map[uint32]struct{}{10: {}},
926 },
927 {
928 name: "element",
929 mod: &Module{
930 ElementSection: []ElementSegment{
931 {
932 Mode: ElementModeActive,
933 Init: []Index{0, ElementInitNullReference, 5},
934 },
935 {
936 Mode: ElementModeDeclarative,
937 Init: []Index{1, ElementInitNullReference, 5},
938 },
939 {
940 Mode: ElementModePassive,
941 Init: []Index{5, 2, ElementInitNullReference, ElementInitNullReference},
942 },
943 },
944 },
945 exp: map[uint32]struct{}{0: {}, 1: {}, 2: {}, 5: {}},
946 },
947 {
948 name: "all",
949 mod: &Module{
950 ExportSection: []Export{
951 {Index: 10, Type: ExternTypeGlobal},
952 {Index: 1000, Type: ExternTypeFunc},
953 },
954 GlobalSection: []Global{
955 {
956 Init: ConstantExpression{
957 Opcode: OpcodeI32Const,
958 Data: leb128.EncodeInt32(-1),
959 },
960 },
961 {
962 Init: ConstantExpression{
963 Opcode: OpcodeRefFunc,
964 Data: leb128.EncodeInt32(123),
965 },
966 },
967 },
968 ElementSection: []ElementSegment{
969 {
970 Mode: ElementModeActive,
971 Init: []Index{0, ElementInitNullReference, 5},
972 },
973 {
974 Mode: ElementModeDeclarative,
975 Init: []Index{1, ElementInitNullReference, 5},
976 },
977 {
978 Mode: ElementModePassive,
979 Init: []Index{5, 2, ElementInitNullReference, ElementInitNullReference},
980 },
981 },
982 },
983 exp: map[uint32]struct{}{0: {}, 1: {}, 2: {}, 5: {}, 123: {}, 1000: {}},
984 },
985 {
986 mod: &Module{
987 GlobalSection: []Global{
988 {
989 Init: ConstantExpression{
990 Opcode: OpcodeRefFunc,
991 Data: nil,
992 },
993 },
994 },
995 },
996 name: "invalid global",
997 expErr: `global[0] failed to initialize: EOF`,
998 },
999 }
1000
1001 for _, tt := range tests {
1002 tc := tt
1003 t.Run(tc.name, func(t *testing.T) {
1004 actual, err := tc.mod.declaredFunctionIndexes()
1005 if tc.expErr != "" {
1006 require.EqualError(t, err, tc.expErr)
1007 } else {
1008 require.NoError(t, err)
1009 require.Equal(t, tc.exp, actual)
1010 }
1011 })
1012 }
1013 }
1014
1015 func TestModule_AssignModuleID(t *testing.T) {
1016 getID := func(bin []byte, lsns []experimental.FunctionListener, withEnsureTermination bool) ModuleID {
1017 m := Module{}
1018 m.AssignModuleID(bin, lsns, withEnsureTermination)
1019 return m.ID
1020 }
1021
1022 ml := &mockListener{}
1023
1024
1025 exists := map[ModuleID]struct{}{}
1026 for i, tc := range []struct {
1027 bin []byte
1028 withEnsureTermination bool
1029 listeners []experimental.FunctionListener
1030 }{
1031 {bin: []byte{1, 2, 3}, withEnsureTermination: false},
1032 {bin: []byte{1, 2, 3}, withEnsureTermination: true},
1033 {
1034 bin: []byte{1, 2, 3},
1035 listeners: []experimental.FunctionListener{ml},
1036 withEnsureTermination: false,
1037 },
1038 {
1039 bin: []byte{1, 2, 3},
1040 listeners: []experimental.FunctionListener{ml},
1041 withEnsureTermination: true,
1042 },
1043 {
1044 bin: []byte{1, 2, 3},
1045 listeners: []experimental.FunctionListener{nil, ml},
1046 withEnsureTermination: true,
1047 },
1048 {
1049 bin: []byte{1, 2, 3},
1050 listeners: []experimental.FunctionListener{ml, ml},
1051 withEnsureTermination: true,
1052 },
1053 {bin: []byte{1, 2, 3, 4}, withEnsureTermination: false},
1054 {bin: []byte{1, 2, 3, 4}, withEnsureTermination: true},
1055 {
1056 bin: []byte{1, 2, 3, 4},
1057 listeners: []experimental.FunctionListener{ml},
1058 withEnsureTermination: false,
1059 },
1060 {
1061 bin: []byte{1, 2, 3, 4},
1062 listeners: []experimental.FunctionListener{ml},
1063 withEnsureTermination: true,
1064 },
1065 {
1066 bin: []byte{1, 2, 3, 4},
1067 listeners: []experimental.FunctionListener{nil},
1068 withEnsureTermination: true,
1069 },
1070 {
1071 bin: []byte{1, 2, 3, 4},
1072 listeners: []experimental.FunctionListener{nil, ml},
1073 withEnsureTermination: true,
1074 },
1075 {
1076 bin: []byte{1, 2, 3, 4},
1077 listeners: []experimental.FunctionListener{ml, ml},
1078 withEnsureTermination: true,
1079 },
1080 {
1081 bin: []byte{1, 2, 3, 4},
1082 listeners: []experimental.FunctionListener{ml, ml},
1083 withEnsureTermination: false,
1084 },
1085 } {
1086 id := getID(tc.bin, tc.listeners, tc.withEnsureTermination)
1087 _, exist := exists[id]
1088 require.False(t, exist, i)
1089 exists[id] = struct{}{}
1090 }
1091 }
1092
1093 type mockListener struct{}
1094
1095 func (m mockListener) Before(context.Context, api.Module, api.FunctionDefinition, []uint64, experimental.StackIterator) {
1096 }
1097
1098 func (m mockListener) After(context.Context, api.Module, api.FunctionDefinition, []uint64) {}
1099
1100 func (m mockListener) Abort(context.Context, api.Module, api.FunctionDefinition, error) {}
1101
View as plain text