1 package wasm
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "math"
8 "strconv"
9 "testing"
10
11 "github.com/tetratelabs/wazero/api"
12 "github.com/tetratelabs/wazero/experimental"
13 "github.com/tetratelabs/wazero/internal/internalapi"
14 "github.com/tetratelabs/wazero/internal/leb128"
15 "github.com/tetratelabs/wazero/internal/sys"
16 "github.com/tetratelabs/wazero/internal/testing/hammer"
17 "github.com/tetratelabs/wazero/internal/testing/require"
18 "github.com/tetratelabs/wazero/internal/u64"
19 )
20
21 func TestModuleInstance_Memory(t *testing.T) {
22 tests := []struct {
23 name string
24 input *Module
25 expected bool
26 expectedLen uint32
27 }{
28 {
29 name: "no memory",
30 input: &Module{},
31 },
32 {
33 name: "memory not exported, one page",
34 input: &Module{
35 MemorySection: &Memory{Min: 1, Cap: 1},
36 MemoryDefinitionSection: []MemoryDefinition{{}},
37 },
38 },
39 {
40 name: "memory exported, different name",
41 input: &Module{
42 MemorySection: &Memory{Min: 1, Cap: 1},
43 MemoryDefinitionSection: []MemoryDefinition{{}},
44 ExportSection: []Export{{Type: ExternTypeMemory, Name: "momory", Index: 0}},
45 },
46 },
47 {
48 name: "memory exported, but zero length",
49 input: &Module{
50 MemorySection: &Memory{},
51 MemoryDefinitionSection: []MemoryDefinition{{}},
52 Exports: map[string]*Export{"memory": {Type: ExternTypeMemory, Name: "memory"}},
53 },
54 expected: true,
55 },
56 {
57 name: "memory exported, one page",
58 input: &Module{
59 MemorySection: &Memory{Min: 1, Cap: 1},
60 MemoryDefinitionSection: []MemoryDefinition{{}},
61 Exports: map[string]*Export{"memory": {Type: ExternTypeMemory, Name: "memory"}},
62 },
63 expected: true,
64 expectedLen: 65536,
65 },
66 {
67 name: "memory exported, two pages",
68 input: &Module{
69 MemorySection: &Memory{Min: 2, Cap: 2},
70 MemoryDefinitionSection: []MemoryDefinition{{}},
71 Exports: map[string]*Export{"memory": {Type: ExternTypeMemory, Name: "memory"}},
72 },
73 expected: true,
74 expectedLen: 65536 * 2,
75 },
76 }
77
78 for _, tt := range tests {
79 tc := tt
80
81 t.Run(tc.name, func(t *testing.T) {
82 s := newStore()
83
84 instance, err := s.Instantiate(testCtx, tc.input, "test", nil, nil)
85 require.NoError(t, err)
86
87 mem := instance.ExportedMemory("memory")
88 if tc.expected {
89 require.Equal(t, tc.expectedLen, mem.Size())
90 } else {
91 require.Nil(t, mem)
92 }
93 })
94 }
95 }
96
97 func TestStore_Instantiate(t *testing.T) {
98 s := newStore()
99 m, err := NewHostModule(
100 "foo",
101 []string{"fn"},
102 map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: func() {}}}},
103 api.CoreFeaturesV1,
104 )
105 require.NoError(t, err)
106
107 sysCtx := sys.DefaultContext(nil)
108 mod, err := s.Instantiate(testCtx, m, "bar", sysCtx, []FunctionTypeID{0})
109 require.NoError(t, err)
110 defer mod.Close(testCtx)
111
112 t.Run("ModuleInstance defaults", func(t *testing.T) {
113 require.Equal(t, s.nameToModule["bar"], mod)
114 require.Equal(t, s.nameToModule["bar"].MemoryInstance, mod.MemoryInstance)
115 require.Equal(t, s, mod.s)
116 require.Equal(t, sysCtx, mod.Sys)
117 })
118 }
119
120 func TestStore_CloseWithExitCode(t *testing.T) {
121 const importedModuleName = "imported"
122 const importingModuleName = "test"
123
124 tests := []struct {
125 name string
126 testClosed bool
127 }{
128 {
129 name: "nothing closed",
130 testClosed: false,
131 },
132 {
133 name: "partially closed",
134 testClosed: true,
135 },
136 }
137
138 for _, tt := range tests {
139 tc := tt
140 t.Run(tc.name, func(t *testing.T) {
141 s := newStore()
142
143 _, err := s.Instantiate(testCtx, &Module{
144 TypeSection: []FunctionType{v_v},
145 FunctionSection: []uint32{0},
146 CodeSection: []Code{{Body: []byte{OpcodeEnd}}},
147 Exports: map[string]*Export{"fn": {Type: ExternTypeFunc, Name: "fn"}},
148 FunctionDefinitionSection: []FunctionDefinition{{Functype: &v_v}},
149 }, importedModuleName, nil, []FunctionTypeID{0})
150 require.NoError(t, err)
151
152 m2, err := s.Instantiate(testCtx, &Module{
153 ImportFunctionCount: 1,
154 TypeSection: []FunctionType{v_v},
155 ImportSection: []Import{{Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0}},
156 MemorySection: &Memory{Min: 1, Cap: 1},
157 MemoryDefinitionSection: []MemoryDefinition{{}},
158 GlobalSection: []Global{{Type: GlobalType{}, Init: ConstantExpression{Opcode: OpcodeI32Const, Data: const1}}},
159 TableSection: []Table{{Min: 10}},
160 }, importingModuleName, nil, []FunctionTypeID{0})
161 require.NoError(t, err)
162
163 if tc.testClosed {
164 err = m2.CloseWithExitCode(testCtx, 2)
165 require.NoError(t, err)
166 }
167
168 err = s.CloseWithExitCode(testCtx, 2)
169 require.NoError(t, err)
170
171
172 require.Nil(t, s.moduleList)
173
174
175 require.Zero(t, len(s.typeIDs))
176 })
177 }
178 }
179
180 func TestStore_hammer(t *testing.T) {
181 const importedModuleName = "imported"
182
183 m, err := NewHostModule(
184 importedModuleName,
185 []string{"fn"},
186 map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: func() {}}}},
187 api.CoreFeaturesV1,
188 )
189 require.NoError(t, err)
190
191 s := newStore()
192 imported, err := s.Instantiate(testCtx, m, importedModuleName, nil, []FunctionTypeID{0})
193 require.NoError(t, err)
194
195 _, ok := s.nameToModule[imported.Name()]
196 require.True(t, ok)
197
198 importingModule := &Module{
199 ImportFunctionCount: 1,
200 TypeSection: []FunctionType{v_v},
201 FunctionSection: []uint32{0},
202 CodeSection: []Code{{Body: []byte{OpcodeEnd}}},
203 MemorySection: &Memory{Min: 1, Cap: 1},
204 MemoryDefinitionSection: []MemoryDefinition{{}},
205 GlobalSection: []Global{{
206 Type: GlobalType{ValType: ValueTypeI32},
207 Init: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(1)},
208 }},
209 TableSection: []Table{{Min: 10}},
210 ImportSection: []Import{
211 {Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0},
212 },
213 }
214
215
216
217 P := 8
218 N := 1000
219 if testing.Short() {
220 P = 4
221 N = 100
222 }
223 hammer.NewHammer(t, P, N).Run(func(name string) {
224 mod, instantiateErr := s.Instantiate(testCtx, importingModule, name, sys.DefaultContext(nil), []FunctionTypeID{0})
225 require.NoError(t, instantiateErr)
226 require.NoError(t, mod.Close(testCtx))
227 }, nil)
228 if t.Failed() {
229 return
230 }
231
232
233 require.NoError(t, imported.Close(testCtx))
234
235
236 require.Nil(t, s.moduleList)
237 }
238
239 func TestStore_hammer_close(t *testing.T) {
240 const importedModuleName = "imported"
241
242 m, err := NewHostModule(
243 importedModuleName,
244 []string{"fn"},
245 map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: func() {}}}},
246 api.CoreFeaturesV1,
247 )
248 require.NoError(t, err)
249
250 s := newStore()
251 imported, err := s.Instantiate(testCtx, m, importedModuleName, nil, []FunctionTypeID{0})
252 require.NoError(t, err)
253
254 _, ok := s.nameToModule[imported.Name()]
255 require.True(t, ok)
256
257 importingModule := &Module{
258 ImportFunctionCount: 1,
259 TypeSection: []FunctionType{v_v},
260 FunctionSection: []uint32{0},
261 CodeSection: []Code{{Body: []byte{OpcodeEnd}}},
262 MemorySection: &Memory{Min: 1, Cap: 1},
263 MemoryDefinitionSection: []MemoryDefinition{{}},
264 GlobalSection: []Global{{
265 Type: GlobalType{ValType: ValueTypeI32},
266 Init: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(1)},
267 }},
268 TableSection: []Table{{Min: 10}},
269 ImportSection: []Import{
270 {Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0},
271 },
272 }
273
274 const instCount = 10000
275 instances := make([]api.Module, instCount)
276 for i := 0; i < instCount; i++ {
277 mod, instantiateErr := s.Instantiate(testCtx, importingModule, strconv.Itoa(i), sys.DefaultContext(nil), []FunctionTypeID{0})
278 require.NoError(t, instantiateErr)
279 instances[i] = mod
280 }
281
282 hammer.NewHammer(t, 100, 2).Run(func(name string) {
283 for i := 0; i < instCount; i++ {
284 if i == instCount/2 {
285
286 err := s.CloseWithExitCode(testCtx, 0)
287 require.NoError(t, err)
288 }
289 err := instances[i].CloseWithExitCode(testCtx, 0)
290 require.NoError(t, err)
291 }
292 require.NoError(t, err)
293 }, nil)
294 if t.Failed() {
295 return
296 }
297
298
299 require.Nil(t, s.moduleList)
300 }
301
302 func TestStore_Instantiate_Errors(t *testing.T) {
303 const importedModuleName = "imported"
304 const importingModuleName = "test"
305
306 m, err := NewHostModule(
307 importedModuleName,
308 []string{"fn"},
309 map[string]*HostFunc{"fn": {ExportName: "fn", Code: Code{GoFunc: func() {}}}},
310 api.CoreFeaturesV1,
311 )
312 require.NoError(t, err)
313
314 t.Run("Fails if module name already in use", func(t *testing.T) {
315 s := newStore()
316 _, err = s.Instantiate(testCtx, m, importedModuleName, nil, []FunctionTypeID{0})
317 require.NoError(t, err)
318
319
320 _, err = s.Instantiate(testCtx, m, importedModuleName, nil, []FunctionTypeID{0})
321 require.EqualError(t, err, "module[imported] has already been instantiated")
322 })
323
324 t.Run("fail resolve import", func(t *testing.T) {
325 s := newStore()
326 _, err = s.Instantiate(testCtx, m, importedModuleName, nil, []FunctionTypeID{0})
327 require.NoError(t, err)
328
329 hm := s.nameToModule[importedModuleName]
330 require.NotNil(t, hm)
331
332 _, err = s.Instantiate(testCtx, &Module{
333 TypeSection: []FunctionType{v_v},
334 ImportSection: []Import{
335
336 {Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0},
337
338 {Type: ExternTypeFunc, Module: "non-exist", Name: "fn", DescFunc: 0},
339 },
340 ImportPerModule: map[string][]*Import{
341 importedModuleName: {{Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0}},
342 "non-exist": {{Name: "fn", DescFunc: 0}},
343 },
344 }, importingModuleName, nil, nil)
345 require.EqualError(t, err, "module[non-exist] not instantiated")
346 })
347
348 t.Run("creating engine failed", func(t *testing.T) {
349 s := newStore()
350
351 _, err = s.Instantiate(testCtx, m, importedModuleName, nil, []FunctionTypeID{0})
352 require.NoError(t, err)
353
354 hm := s.nameToModule[importedModuleName]
355 require.NotNil(t, hm)
356
357 engine := s.Engine.(*mockEngine)
358 engine.shouldCompileFail = true
359
360 importingModule := &Module{
361 ImportFunctionCount: 1,
362 TypeSection: []FunctionType{v_v},
363 FunctionSection: []uint32{0, 0},
364 CodeSection: []Code{
365 {Body: []byte{OpcodeEnd}},
366 {Body: []byte{OpcodeEnd}},
367 },
368 ImportSection: []Import{
369 {Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0},
370 },
371 }
372
373 _, err = s.Instantiate(testCtx, importingModule, importingModuleName, nil, []FunctionTypeID{0})
374 require.EqualError(t, err, "some engine creation error")
375 })
376
377 t.Run("start func failed", func(t *testing.T) {
378 s := newStore()
379 engine := s.Engine.(*mockEngine)
380 engine.callFailIndex = 1
381
382 _, err = s.Instantiate(testCtx, m, importedModuleName, nil, []FunctionTypeID{0})
383 require.NoError(t, err)
384
385 hm := s.nameToModule[importedModuleName]
386 require.NotNil(t, hm)
387
388 startFuncIndex := uint32(1)
389 importingModule := &Module{
390 ImportFunctionCount: 1,
391 TypeSection: []FunctionType{v_v},
392 FunctionSection: []uint32{0},
393 CodeSection: []Code{{Body: []byte{OpcodeEnd}}},
394 StartSection: &startFuncIndex,
395 ImportSection: []Import{
396 {Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0},
397 },
398 }
399
400 _, err = s.Instantiate(testCtx, importingModule, importingModuleName, nil, []FunctionTypeID{0})
401 require.EqualError(t, err, "start function[1] failed: call failed")
402 })
403 }
404
405 type mockEngine struct {
406 shouldCompileFail bool
407 callFailIndex int
408 }
409
410 type mockModuleEngine struct {
411 name string
412 callFailIndex int
413 functionRefs map[Index]Reference
414 resolveImportsCalled map[Index]Index
415 importedMemModEngine ModuleEngine
416 lookupEntries map[Index]mockModuleEngineLookupEntry
417 }
418
419 type mockModuleEngineLookupEntry struct {
420 m *ModuleInstance
421 index Index
422 }
423
424 type mockCallEngine struct {
425 internalapi.WazeroOnlyType
426 index Index
427 callFailIndex int
428 }
429
430 func newStore() *Store {
431 return NewStore(api.CoreFeaturesV1, &mockEngine{shouldCompileFail: false, callFailIndex: -1})
432 }
433
434
435 func (e *mockEngine) Close() error {
436 return nil
437 }
438
439
440 func (e *mockEngine) CompileModule(context.Context, *Module, []experimental.FunctionListener, bool) error {
441 return nil
442 }
443
444
445 func (e *mockModuleEngine) LookupFunction(_ *TableInstance, _ FunctionTypeID, offset Index) (*ModuleInstance, Index) {
446 if entry, ok := e.lookupEntries[offset]; ok {
447 return entry.m, entry.index
448 }
449 return nil, 0
450 }
451
452
453 func (e *mockEngine) CompiledModuleCount() uint32 { return 0 }
454
455
456 func (e *mockEngine) DeleteCompiledModule(*Module) {}
457
458
459 func (e *mockEngine) NewModuleEngine(_ *Module, _ *ModuleInstance) (ModuleEngine, error) {
460 if e.shouldCompileFail {
461 return nil, fmt.Errorf("some engine creation error")
462 }
463 return &mockModuleEngine{callFailIndex: e.callFailIndex, resolveImportsCalled: map[Index]Index{}}, nil
464 }
465
466
467 func (e *mockModuleEngine) GetGlobalValue(idx Index) (lo, hi uint64) { panic("BUG") }
468
469
470 func (e *mockModuleEngine) OwnsGlobals() bool { return false }
471
472
473 func (e *mockModuleEngine) DoneInstantiation() {}
474
475
476 func (e *mockModuleEngine) FunctionInstanceReference(i Index) Reference {
477 return e.functionRefs[i]
478 }
479
480
481 func (e *mockModuleEngine) ResolveImportedFunction(index, importedIndex Index, _ ModuleEngine) {
482 e.resolveImportsCalled[index] = importedIndex
483 }
484
485
486 func (e *mockModuleEngine) ResolveImportedMemory(imp ModuleEngine) {
487 e.importedMemModEngine = imp
488 }
489
490
491 func (e *mockModuleEngine) NewFunction(index Index) api.Function {
492 return &mockCallEngine{index: index, callFailIndex: e.callFailIndex}
493 }
494
495
496 func (e *mockModuleEngine) InitializeFuncrefGlobals(globals []*GlobalInstance) {}
497
498
499 func (e *mockModuleEngine) Name() string {
500 return e.name
501 }
502
503
504 func (e *mockModuleEngine) Close(context.Context) {
505 }
506
507
508 func (ce *mockCallEngine) Definition() api.FunctionDefinition { return nil }
509
510
511 func (ce *mockCallEngine) Call(ctx context.Context, _ ...uint64) (results []uint64, err error) {
512 return nil, ce.CallWithStack(ctx, nil)
513 }
514
515
516 func (ce *mockCallEngine) CallWithStack(_ context.Context, _ []uint64) error {
517 if ce.callFailIndex >= 0 && ce.index == Index(ce.callFailIndex) {
518 return errors.New("call failed")
519 }
520 return nil
521 }
522
523 func TestStore_getFunctionTypeID(t *testing.T) {
524 t.Run("too many functions", func(t *testing.T) {
525 s := newStore()
526 const max = 10
527 s.functionMaxTypes = max
528 s.typeIDs = make(map[string]FunctionTypeID)
529 for i := 0; i < max; i++ {
530 s.typeIDs[strconv.Itoa(i)] = 0
531 }
532 _, err := s.GetFunctionTypeID(&FunctionType{})
533 require.Error(t, err)
534 })
535 t.Run("ok", func(t *testing.T) {
536 tests := []FunctionType{
537 {Params: []ValueType{}},
538 {Params: []ValueType{ValueTypeF32}},
539 {Results: []ValueType{ValueTypeF64}},
540 {Params: []ValueType{ValueTypeI32}, Results: []ValueType{ValueTypeI64}},
541 }
542
543 for _, tt := range tests {
544 tc := tt
545 t.Run(tc.String(), func(t *testing.T) {
546 s := newStore()
547 actual, err := s.GetFunctionTypeID(&tc)
548 require.NoError(t, err)
549
550 expectedTypeID, ok := s.typeIDs[tc.String()]
551 require.True(t, ok)
552 require.Equal(t, expectedTypeID, actual)
553 })
554 }
555 })
556 }
557
558 func TestGlobalInstance_initialize(t *testing.T) {
559 t.Run("basic type const expr", func(t *testing.T) {
560 for _, vt := range []ValueType{ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64} {
561 t.Run(ValueTypeName(vt), func(t *testing.T) {
562 g := &GlobalInstance{Type: GlobalType{ValType: vt}}
563 expr := &ConstantExpression{}
564 switch vt {
565 case ValueTypeI32:
566 expr.Data = []byte{1}
567 expr.Opcode = OpcodeI32Const
568 case ValueTypeI64:
569 expr.Data = []byte{2}
570 expr.Opcode = OpcodeI64Const
571 case ValueTypeF32:
572 expr.Data = u64.LeBytes(api.EncodeF32(math.MaxFloat32))
573 expr.Opcode = OpcodeF32Const
574 case ValueTypeF64:
575 expr.Data = u64.LeBytes(api.EncodeF64(math.MaxFloat64))
576 expr.Opcode = OpcodeF64Const
577 }
578
579 g.initialize(nil, expr, nil)
580
581 switch vt {
582 case ValueTypeI32:
583 require.Equal(t, int32(1), int32(g.Val))
584 case ValueTypeI64:
585 require.Equal(t, int64(2), int64(g.Val))
586 case ValueTypeF32:
587 require.Equal(t, float32(math.MaxFloat32), math.Float32frombits(uint32(g.Val)))
588 case ValueTypeF64:
589 require.Equal(t, math.MaxFloat64, math.Float64frombits(g.Val))
590 }
591 })
592 }
593 })
594 t.Run("ref.null", func(t *testing.T) {
595 tests := []struct {
596 name string
597 expr *ConstantExpression
598 }{
599 {
600 name: "ref.null (externref)",
601 expr: &ConstantExpression{
602 Opcode: OpcodeRefNull,
603 Data: []byte{RefTypeExternref},
604 },
605 },
606 {
607 name: "ref.null (funcref)",
608 expr: &ConstantExpression{
609 Opcode: OpcodeRefNull,
610 Data: []byte{RefTypeFuncref},
611 },
612 },
613 }
614
615 for _, tt := range tests {
616 tc := tt
617 t.Run(tc.name, func(t *testing.T) {
618 g := GlobalInstance{}
619 g.Type.ValType = tc.expr.Data[0]
620 g.initialize(nil, tc.expr, nil)
621 require.Equal(t, uint64(0), g.Val)
622 })
623 }
624 })
625 t.Run("ref.func", func(t *testing.T) {
626 g := GlobalInstance{Type: GlobalType{ValType: RefTypeFuncref}}
627 g.initialize(nil,
628 &ConstantExpression{Opcode: OpcodeRefFunc, Data: []byte{1}},
629 func(funcIndex Index) Reference {
630 require.Equal(t, Index(1), funcIndex)
631 return 0xdeadbeaf
632 },
633 )
634 require.Equal(t, uint64(0xdeadbeaf), g.Val)
635 })
636 t.Run("global expr", func(t *testing.T) {
637 tests := []struct {
638 valueType ValueType
639 val, valHi uint64
640 }{
641 {valueType: ValueTypeI32, val: 10},
642 {valueType: ValueTypeI64, val: 20},
643 {valueType: ValueTypeF32, val: uint64(math.Float32bits(634634432.12311))},
644 {valueType: ValueTypeF64, val: math.Float64bits(1.12312311)},
645 {valueType: ValueTypeV128, val: 0x1, valHi: 0x2},
646 {valueType: ValueTypeExternref, val: 0x12345},
647 {valueType: ValueTypeFuncref, val: 0x54321},
648 }
649
650 for _, tt := range tests {
651 tc := tt
652 t.Run(ValueTypeName(tc.valueType), func(t *testing.T) {
653
654 expr := &ConstantExpression{Data: []byte{0}, Opcode: OpcodeGlobalGet}
655 globals := []*GlobalInstance{{Val: tc.val, ValHi: tc.valHi, Type: GlobalType{ValType: tc.valueType}}}
656
657 g := &GlobalInstance{Type: GlobalType{ValType: tc.valueType}}
658 g.initialize(globals, expr, nil)
659
660 switch tc.valueType {
661 case ValueTypeI32:
662 require.Equal(t, int32(tc.val), int32(g.Val))
663 case ValueTypeI64:
664 require.Equal(t, int64(tc.val), int64(g.Val))
665 case ValueTypeF32:
666 require.Equal(t, tc.val, g.Val)
667 case ValueTypeF64:
668 require.Equal(t, tc.val, g.Val)
669 case ValueTypeV128:
670 require.Equal(t, uint64(0x1), g.Val)
671 require.Equal(t, uint64(0x2), g.ValHi)
672 case ValueTypeFuncref, ValueTypeExternref:
673 require.Equal(t, tc.val, g.Val)
674 }
675 })
676 }
677 })
678
679 t.Run("vector", func(t *testing.T) {
680 expr := &ConstantExpression{Data: []byte{
681 1, 0, 0, 0, 0, 0, 0, 0,
682 2, 0, 0, 0, 0, 0, 0, 0,
683 }, Opcode: OpcodeVecV128Const}
684 g := GlobalInstance{Type: GlobalType{ValType: ValueTypeV128}}
685 g.initialize(nil, expr, nil)
686 require.Equal(t, uint64(0x1), g.Val)
687 require.Equal(t, uint64(0x2), g.ValHi)
688 })
689 }
690
691 func Test_resolveImports(t *testing.T) {
692 const moduleName = "test"
693 const name = "target"
694
695 t.Run("module not instantiated", func(t *testing.T) {
696 m := &ModuleInstance{s: newStore()}
697 err := m.resolveImports(&Module{ImportPerModule: map[string][]*Import{"unknown": {{}}}})
698 require.EqualError(t, err, "module[unknown] not instantiated")
699 })
700 t.Run("export instance not found", func(t *testing.T) {
701 m := &ModuleInstance{s: newStore()}
702 m.s.nameToModule[moduleName] = &ModuleInstance{Exports: map[string]*Export{}, ModuleName: moduleName}
703 err := m.resolveImports(&Module{ImportPerModule: map[string][]*Import{moduleName: {{Name: "unknown"}}}})
704 require.EqualError(t, err, "\"unknown\" is not exported in module \"test\"")
705 })
706 t.Run("func", func(t *testing.T) {
707 t.Run("ok", func(t *testing.T) {
708 s := newStore()
709 s.nameToModule[moduleName] = &ModuleInstance{
710 Exports: map[string]*Export{
711 name: {Type: ExternTypeFunc, Index: 2},
712 "": {Type: ExternTypeFunc, Index: 4},
713 },
714 ModuleName: moduleName,
715 Source: &Module{
716 FunctionSection: []Index{0, 0, 1, 0, 0},
717 TypeSection: []FunctionType{
718 {Params: []ValueType{ExternTypeFunc}},
719 {Params: []ValueType{i32}, Results: []ValueType{ValueTypeV128}},
720 },
721 },
722 }
723
724 module := &Module{
725 TypeSection: []FunctionType{
726 {Params: []ValueType{i32}, Results: []ValueType{ValueTypeV128}},
727 {Params: []ValueType{ExternTypeFunc}},
728 },
729 ImportFunctionCount: 2,
730 ImportPerModule: map[string][]*Import{
731 moduleName: {
732 {Module: moduleName, Name: name, Type: ExternTypeFunc, DescFunc: 0, IndexPerType: 0},
733 {Module: moduleName, Name: "", Type: ExternTypeFunc, DescFunc: 1, IndexPerType: 1},
734 },
735 },
736 }
737
738 m := &ModuleInstance{Engine: &mockModuleEngine{resolveImportsCalled: map[Index]Index{}}, s: s, Source: module}
739 err := m.resolveImports(module)
740 require.NoError(t, err)
741
742 me := m.Engine.(*mockModuleEngine)
743 require.Equal(t, me.resolveImportsCalled[0], Index(2))
744 require.Equal(t, me.resolveImportsCalled[1], Index(4))
745 })
746 t.Run("signature mismatch", func(t *testing.T) {
747 s := newStore()
748 s.nameToModule[moduleName] = &ModuleInstance{
749 Exports: map[string]*Export{
750 name: {Type: ExternTypeFunc, Index: 0},
751 },
752 ModuleName: moduleName,
753 TypeIDs: []FunctionTypeID{123435},
754 Source: &Module{
755 FunctionSection: []Index{0},
756 TypeSection: []FunctionType{
757 {Params: []ValueType{}},
758 },
759 },
760 }
761 module := &Module{
762 TypeSection: []FunctionType{{Results: []ValueType{ValueTypeF32}}},
763 ImportPerModule: map[string][]*Import{
764 moduleName: {{Module: moduleName, Name: name, Type: ExternTypeFunc, DescFunc: 0}},
765 },
766 }
767
768 m := &ModuleInstance{Engine: &mockModuleEngine{resolveImportsCalled: map[Index]Index{}}, s: s, Source: module}
769 err := m.resolveImports(module)
770 require.EqualError(t, err, "import func[test.target]: signature mismatch: v_f32 != v_v")
771 })
772 })
773 t.Run("global", func(t *testing.T) {
774 t.Run("ok", func(t *testing.T) {
775 s := newStore()
776 g := &GlobalInstance{Type: GlobalType{ValType: ValueTypeI32}}
777 m := &ModuleInstance{Globals: make([]*GlobalInstance, 1), s: s}
778 s.nameToModule[moduleName] = &ModuleInstance{
779 Globals: []*GlobalInstance{g},
780 Exports: map[string]*Export{name: {Type: ExternTypeGlobal, Index: 0}}, ModuleName: moduleName,
781 }
782 err := m.resolveImports(
783 &Module{
784 ImportPerModule: map[string][]*Import{moduleName: {{Name: name, Type: ExternTypeGlobal, DescGlobal: g.Type}}},
785 },
786 )
787 require.NoError(t, err)
788 require.True(t, globalsContain(m.Globals, g), "expected to find %v in %v", g, m.Globals)
789 })
790 t.Run("mutability mismatch", func(t *testing.T) {
791 s := newStore()
792 s.nameToModule[moduleName] = &ModuleInstance{
793 Globals: []*GlobalInstance{{Type: GlobalType{Mutable: false}}},
794 Exports: map[string]*Export{name: {
795 Type: ExternTypeGlobal,
796 Index: 0,
797 }},
798 ModuleName: moduleName,
799 }
800 m := &ModuleInstance{Globals: make([]*GlobalInstance, 1), s: s}
801 err := m.resolveImports(&Module{
802 ImportPerModule: map[string][]*Import{moduleName: {
803 {Module: moduleName, Name: name, Type: ExternTypeGlobal, DescGlobal: GlobalType{Mutable: true}},
804 }},
805 })
806 require.EqualError(t, err, "import global[test.target]: mutability mismatch: true != false")
807 })
808 t.Run("type mismatch", func(t *testing.T) {
809 s := newStore()
810 s.nameToModule[moduleName] = &ModuleInstance{
811 Globals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}}},
812 Exports: map[string]*Export{name: {
813 Type: ExternTypeGlobal,
814 Index: 0,
815 }},
816 ModuleName: moduleName,
817 }
818 m := &ModuleInstance{Globals: make([]*GlobalInstance, 1), s: s}
819 err := m.resolveImports(&Module{
820 ImportPerModule: map[string][]*Import{moduleName: {
821 {Module: moduleName, Name: name, Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeF64}},
822 }},
823 })
824 require.EqualError(t, err, "import global[test.target]: value type mismatch: f64 != i32")
825 })
826 })
827 t.Run("memory", func(t *testing.T) {
828 t.Run("ok", func(t *testing.T) {
829 max := uint32(10)
830 memoryInst := &MemoryInstance{Max: max}
831 s := newStore()
832 importedME := &mockModuleEngine{}
833 s.nameToModule[moduleName] = &ModuleInstance{
834 MemoryInstance: memoryInst,
835 Exports: map[string]*Export{name: {
836 Type: ExternTypeMemory,
837 }},
838 ModuleName: moduleName,
839 Engine: importedME,
840 }
841 m := &ModuleInstance{s: s, Engine: &mockModuleEngine{resolveImportsCalled: map[Index]Index{}}}
842 err := m.resolveImports(&Module{
843 ImportPerModule: map[string][]*Import{
844 moduleName: {{Module: moduleName, Name: name, Type: ExternTypeMemory, DescMem: &Memory{Max: max}}},
845 },
846 })
847 require.NoError(t, err)
848 require.Equal(t, m.MemoryInstance, memoryInst)
849 require.Equal(t, importedME, m.Engine.(*mockModuleEngine).importedMemModEngine)
850 })
851 t.Run("minimum size mismatch", func(t *testing.T) {
852 importMemoryType := &Memory{Min: 2, Cap: 2}
853 s := newStore()
854 s.nameToModule[moduleName] = &ModuleInstance{
855 MemoryInstance: &MemoryInstance{Min: importMemoryType.Min - 1, Cap: 2},
856 Exports: map[string]*Export{name: {
857 Type: ExternTypeMemory,
858 }},
859 ModuleName: moduleName,
860 }
861 m := &ModuleInstance{s: s}
862 err := m.resolveImports(&Module{
863 ImportPerModule: map[string][]*Import{
864 moduleName: {{Module: moduleName, Name: name, Type: ExternTypeMemory, DescMem: importMemoryType}},
865 },
866 })
867 require.EqualError(t, err, "import memory[test.target]: minimum size mismatch: 2 > 1")
868 })
869 t.Run("maximum size mismatch", func(t *testing.T) {
870 s := newStore()
871 s.nameToModule[moduleName] = &ModuleInstance{
872 MemoryInstance: &MemoryInstance{Max: MemoryLimitPages},
873 Exports: map[string]*Export{name: {
874 Type: ExternTypeMemory,
875 }},
876 ModuleName: moduleName,
877 }
878
879 max := uint32(10)
880 importMemoryType := &Memory{Max: max}
881 m := &ModuleInstance{s: s}
882 err := m.resolveImports(&Module{
883 ImportPerModule: map[string][]*Import{moduleName: {{Module: moduleName, Name: name, Type: ExternTypeMemory, DescMem: importMemoryType}}},
884 })
885 require.EqualError(t, err, "import memory[test.target]: maximum size mismatch: 10 < 65536")
886 })
887 })
888 }
889
890 func TestModuleInstance_validateData(t *testing.T) {
891 m := &ModuleInstance{MemoryInstance: &MemoryInstance{Buffer: make([]byte, 5)}}
892 tests := []struct {
893 name string
894 data []DataSegment
895 expErr string
896 }{
897 {
898 name: "ok",
899 data: []DataSegment{
900 {OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []byte{0}},
901 {OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)}, Init: []byte{0}},
902 },
903 },
904 {
905 name: "out of bounds - single one byte",
906 data: []DataSegment{
907 {OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(5)}, Init: []byte{0}},
908 },
909 expErr: "data[0]: out of bounds memory access",
910 },
911 {
912 name: "out of bounds - multi bytes",
913 data: []DataSegment{
914 {OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(0)}, Init: []byte{0}},
915 {OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(3)}, Init: []byte{0, 1, 2}},
916 },
917 expErr: "data[1]: out of bounds memory access",
918 },
919 }
920
921 for _, tt := range tests {
922 tc := tt
923 t.Run(tc.name, func(t *testing.T) {
924 err := m.validateData(tc.data)
925 if tc.expErr != "" {
926 require.EqualError(t, err, tc.expErr)
927 } else {
928 require.NoError(t, err)
929 }
930 })
931 }
932 }
933
934 func TestModuleInstance_applyData(t *testing.T) {
935 t.Run("ok", func(t *testing.T) {
936 m := &ModuleInstance{MemoryInstance: &MemoryInstance{Buffer: make([]byte, 10)}}
937 err := m.applyData([]DataSegment{
938 {OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: const0}, Init: []byte{0xa, 0xf}},
939 {OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeUint32(8)}, Init: []byte{0x1, 0x5}},
940 })
941 require.NoError(t, err)
942 require.Equal(t, []byte{0xa, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5}, m.MemoryInstance.Buffer)
943 require.Equal(t, [][]byte{{0xa, 0xf}, {0x1, 0x5}}, m.DataInstances)
944 })
945 t.Run("error", func(t *testing.T) {
946 m := &ModuleInstance{MemoryInstance: &MemoryInstance{Buffer: make([]byte, 5)}}
947 err := m.applyData([]DataSegment{
948 {OffsetExpression: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeUint32(8)}, Init: []byte{}},
949 })
950 require.EqualError(t, err, "data[0]: out of bounds memory access")
951 })
952 }
953
954 func globalsContain(globals []*GlobalInstance, want *GlobalInstance) bool {
955 for _, f := range globals {
956 if f == want {
957 return true
958 }
959 }
960 return false
961 }
962
963 func TestModuleInstance_applyElements(t *testing.T) {
964 leb128_100 := leb128.EncodeInt32(100)
965
966 t.Run("extenref", func(t *testing.T) {
967 m := &ModuleInstance{}
968 m.Tables = []*TableInstance{{Type: RefTypeExternref, References: make([]Reference, 10)}}
969 for i := range m.Tables[0].References {
970 m.Tables[0].References[i] = 0xffff
971 }
972
973
974 m.applyElements([]ElementSegment{{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128_100}}})
975 m.applyElements([]ElementSegment{
976 {Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: make([]Index, 3)},
977 {Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128_100}, Init: make([]Index, 5)},
978 {Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: make([]Index, 5)},
979 })
980 require.Equal(t, []Reference{0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
981 m.Tables[0].References)
982 m.applyElements([]ElementSegment{
983 {Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: make([]Index, 5)},
984 })
985 require.Equal(t, []Reference{0, 0, 0, 0xffff, 0xffff, 0, 0, 0, 0, 0}, m.Tables[0].References)
986 })
987 t.Run("funcref", func(t *testing.T) {
988 e := &mockEngine{}
989 me, err := e.NewModuleEngine(nil, nil)
990 me.(*mockModuleEngine).functionRefs = map[Index]Reference{0: 0xa, 1: 0xaa, 2: 0xaaa, 3: 0xaaaa}
991 require.NoError(t, err)
992 m := &ModuleInstance{Engine: me, Globals: []*GlobalInstance{{}, {Val: 0xabcde}}}
993
994 m.Tables = []*TableInstance{{Type: RefTypeFuncref, References: make([]Reference, 10)}}
995 for i := range m.Tables[0].References {
996 m.Tables[0].References[i] = 0xffff
997 }
998
999
1000 m.applyElements([]ElementSegment{{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128_100}, Init: []Index{1, 2, 3}}})
1001 m.applyElements([]ElementSegment{
1002 {Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0, 1, 2}},
1003 {Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{9}}, Init: []Index{1 | ElementInitImportedGlobalFunctionReference}},
1004 {Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128_100}, Init: make([]Index, 5)},
1005 {Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: make([]Index, 5)},
1006 })
1007 require.Equal(t, []Reference{0xa, 0xaa, 0xaaa, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xabcde},
1008 m.Tables[0].References)
1009 m.applyElements([]ElementSegment{
1010 {Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: []Index{0, ElementInitNullReference, 2}},
1011 })
1012 require.Equal(t, []Reference{0xa, 0xaa, 0xaaa, 0xffff, 0xffff, 0xa, 0xffff, 0xaaa, 0xffff, 0xabcde},
1013 m.Tables[0].References)
1014 })
1015 }
1016
View as plain text