github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/wasm/host_test.go (about) 1 package wasm 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/wasilibs/wazerox/api" 8 "github.com/wasilibs/wazerox/internal/testing/require" 9 . "github.com/wasilibs/wazerox/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 // `require.Equal(t, expected, actual)` fails reflect pointers don't match, so brute compare: 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 // Special case because reflect.Value can't be compared with Equals 143 // TODO: This is copy/paste with /builder_test.go 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 // Not wasm 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 }