wa-lang.org/wazero@v1.0.2/internal/wasm/binary/names_test.go (about) 1 package binary 2 3 import ( 4 "bytes" 5 "testing" 6 7 "wa-lang.org/wazero/internal/testing/require" 8 "wa-lang.org/wazero/internal/wasm" 9 ) 10 11 func TestEncodeNameSectionData(t *testing.T) { 12 tests := []struct { 13 name string 14 input *wasm.NameSection 15 expected []byte 16 }{ 17 { 18 name: "empty", 19 input: &wasm.NameSection{}, 20 }, 21 { 22 name: "only module", 23 // e.g. (module $simple ) 24 input: &wasm.NameSection{ModuleName: "simple"}, 25 expected: []byte{ 26 subsectionIDModuleName, 0x07, // 7 bytes 27 0x06, // the Module name simple is 6 bytes long 28 's', 'i', 'm', 'p', 'l', 'e', 29 }, 30 }, 31 { 32 name: "module and function name", 33 // (module $simple 34 // (import "" "Hello" (func $hello)) 35 // (start $hello) 36 // ) 37 input: &wasm.NameSection{ 38 ModuleName: "simple", 39 FunctionNames: wasm.NameMap{{Index: wasm.Index(0), Name: "hello"}}, 40 }, 41 expected: []byte{ 42 subsectionIDModuleName, 0x07, // 7 bytes 43 0x06, // the Module name simple is 6 bytes long 44 's', 'i', 'm', 'p', 'l', 'e', 45 subsectionIDFunctionNames, 0x08, // 8 bytes 46 0x01, // one function name 47 0x00, // the function index is zero 48 0x05, // the function name hello is 5 bytes long 49 'h', 'e', 'l', 'l', 'o', 50 }, 51 }, 52 { 53 name: "two function names", // e.g. TinyGo which at one point didn't set a module name 54 // (module 55 // (import "wasi_snapshot_preview1" "args_sizes_get" (func $wasi.args_sizes_get (param i32, i32) (result i32))) 56 // (import "wasi_snapshot_preview1" "fd_write" (func $wasi.fd_write (param i32, i32, i32, i32) (result i32))) 57 // ) 58 input: &wasm.NameSection{ 59 FunctionNames: wasm.NameMap{ 60 {Index: wasm.Index(0), Name: "wasi.args_sizes_get"}, 61 {Index: wasm.Index(1), Name: "wasi.fd_write"}, 62 }, 63 }, 64 expected: []byte{ 65 subsectionIDFunctionNames, 0x25, // 37 bytes 66 0x02, // two function names 67 0x00, // the function index is zero 68 0x13, // the function name wasi.args_sizes_get is 19 bytes long 69 'w', 'a', 's', 'i', '.', 'a', 'r', 'g', 's', '_', 's', 'i', 'z', 'e', 's', '_', 'g', 'e', 't', 70 0x01, // the function index is one 71 0x0d, // the function name wasi.fd_write is 13 bytes long 72 'w', 'a', 's', 'i', '.', 'f', 'd', '_', 'w', 'r', 'i', 't', 'e', 73 }, 74 }, 75 { 76 name: "function with local names", 77 // (module 78 // (import "Math" "Mul" (func $mul (param $x f32) (param $y f32) (result f32))) 79 // (import "Math" "Add" (func $add (param $l f32) (param $r f32) (result f32))) 80 // ) 81 input: &wasm.NameSection{ 82 FunctionNames: wasm.NameMap{ 83 {Index: wasm.Index(0), Name: "mul"}, 84 {Index: wasm.Index(1), Name: "add"}, 85 }, 86 LocalNames: wasm.IndirectNameMap{ 87 {Index: wasm.Index(0), NameMap: wasm.NameMap{ 88 {Index: wasm.Index(0), Name: "x"}, 89 {Index: wasm.Index(1), Name: "y"}, 90 }}, 91 {Index: wasm.Index(1), NameMap: wasm.NameMap{ 92 {Index: wasm.Index(0), Name: "l"}, 93 {Index: wasm.Index(1), Name: "r"}, 94 }}, 95 }, 96 }, 97 expected: []byte{ 98 subsectionIDFunctionNames, 0x0b, // 7 bytes 99 0x02, // two function names 100 0x00, 0x03, 'm', 'u', 'l', // index 0, size of "mul", "mul" 101 0x01, 0x03, 'a', 'd', 'd', // index 1, size of "add", "add" 102 subsectionIDLocalNames, 0x11, // 17 bytes 103 0x02, // two functions 104 0x00, 0x02, // index 0 has 2 locals 105 0x00, 0x01, 'x', // index 0, size of "x", "x" 106 0x01, 0x01, 'y', // index 1, size of "y", "y" 107 0x01, 0x02, // index 1 has 2 locals 108 0x00, 0x01, 'l', // index 0, size of "l", "l" 109 0x01, 0x01, 'r', // index 1, size of "r", "r" 110 }, 111 }, 112 } 113 114 for _, tt := range tests { 115 tc := tt 116 117 t.Run(tc.name, func(t *testing.T) { 118 bytes := encodeNameSectionData(tc.input) 119 require.Equal(t, tc.expected, bytes) 120 }) 121 } 122 } 123 124 func TestEncodeNameSubsection(t *testing.T) { 125 subsectionID := uint8(1) 126 name := []byte("simple") 127 require.Equal(t, []byte{ 128 subsectionID, 129 byte(1 + 6), // 1 is the size of 6 in LEB128 encoding 130 6, 's', 'i', 'm', 'p', 'l', 'e', 131 }, encodeNameSubsection(subsectionID, encodeSizePrefixed(name))) 132 } 133 134 func TestEncodeNameAssoc(t *testing.T) { 135 na := &wasm.NameAssoc{Index: 1, Name: "hello"} 136 require.Equal(t, []byte{byte(na.Index), 5, 'h', 'e', 'l', 'l', 'o'}, encodeNameAssoc(na)) 137 } 138 139 func TestEncodeNameMap(t *testing.T) { 140 na := &wasm.NameAssoc{Index: 1, Name: "hello"} 141 m := wasm.NameMap{na} 142 require.Equal(t, []byte{byte(1), byte(na.Index), 5, 'h', 'e', 'l', 'l', 'o'}, encodeNameMap(m)) 143 } 144 145 func TestEncodeSizePrefixed(t *testing.T) { 146 // We expect size in bytes (LEB128 encoded) then the bytes 147 require.Equal(t, []byte{5, 'h', 'e', 'l', 'l', 'o'}, encodeSizePrefixed([]byte("hello"))) 148 } 149 150 // TestDecodeNameSection relies on unit tests for NameSection.EncodeData, specifically that the encoding is 151 // both known and correct. This avoids having to copy/paste or share variables to assert against byte arrays. 152 func TestDecodeNameSection(t *testing.T) { 153 tests := []struct { 154 name string 155 input *wasm.NameSection // round trip test! 156 }{ 157 { 158 name: "empty", 159 input: &wasm.NameSection{}, 160 }, 161 { 162 name: "only module", 163 input: &wasm.NameSection{ModuleName: "simple"}, 164 }, 165 { 166 name: "module and function name", 167 input: &wasm.NameSection{ 168 ModuleName: "simple", 169 FunctionNames: wasm.NameMap{{Index: wasm.Index(0), Name: "wasi.hello"}}, 170 }, 171 }, 172 { 173 name: "two function names", 174 input: &wasm.NameSection{ 175 FunctionNames: wasm.NameMap{ 176 {Index: wasm.Index(0), Name: "wasi.args_sizes_get"}, 177 {Index: wasm.Index(1), Name: "wasi.fd_write"}, 178 }, 179 }, 180 }, 181 { 182 name: "function with local names", 183 input: &wasm.NameSection{ 184 FunctionNames: wasm.NameMap{ 185 {Index: wasm.Index(0), Name: "mul"}, 186 {Index: wasm.Index(1), Name: "add"}, 187 }, 188 LocalNames: wasm.IndirectNameMap{ 189 {Index: wasm.Index(0), NameMap: wasm.NameMap{ 190 {Index: wasm.Index(0), Name: "x"}, 191 {Index: wasm.Index(1), Name: "y"}, 192 }}, 193 {Index: wasm.Index(1), NameMap: wasm.NameMap{ 194 {Index: wasm.Index(0), Name: "l"}, 195 {Index: wasm.Index(1), Name: "r"}, 196 }}, 197 }, 198 }, 199 }, 200 } 201 202 for _, tt := range tests { 203 tc := tt 204 205 t.Run(tc.name, func(t *testing.T) { 206 data := encodeNameSectionData(tc.input) 207 ns, err := decodeNameSection(bytes.NewReader(data), uint64(len(data))) 208 require.NoError(t, err) 209 require.Equal(t, tc.input, ns) 210 }) 211 } 212 } 213 214 func TestDecodeNameSection_Errors(t *testing.T) { 215 // currently, we ignore the size of known subsections 216 ignoredSubsectionSize := byte(50) 217 tests := []struct { 218 name string 219 input []byte 220 expectedErr string 221 }{ 222 { 223 name: "EOF after module name subsection ID", 224 input: []byte{subsectionIDModuleName}, 225 expectedErr: "failed to read the size of subsection[0]: EOF", 226 }, 227 { 228 name: "EOF after function names subsection ID", 229 input: []byte{subsectionIDFunctionNames}, 230 expectedErr: "failed to read the size of subsection[1]: EOF", 231 }, 232 { 233 name: "EOF after local names subsection ID", 234 input: []byte{subsectionIDLocalNames}, 235 expectedErr: "failed to read the size of subsection[2]: EOF", 236 }, 237 { 238 name: "EOF after unknown subsection ID", 239 input: []byte{4}, 240 expectedErr: "failed to read the size of subsection[4]: EOF", 241 }, 242 { 243 name: "EOF after module name subsection size", 244 input: []byte{subsectionIDModuleName, ignoredSubsectionSize}, 245 expectedErr: "failed to read module name size: EOF", 246 }, 247 { 248 name: "EOF after function names subsection size", 249 input: []byte{subsectionIDFunctionNames, ignoredSubsectionSize}, 250 expectedErr: "failed to read the function count of subsection[1]: EOF", 251 }, 252 { 253 name: "EOF after local names subsection size", 254 input: []byte{subsectionIDLocalNames, ignoredSubsectionSize}, 255 expectedErr: "failed to read the function count of subsection[2]: EOF", 256 }, 257 { 258 name: "EOF skipping unknown subsection size", 259 input: []byte{4, 100}, 260 expectedErr: "failed to skip subsection[4]: EOF", 261 }, 262 { 263 name: "EOF after module name size", 264 input: []byte{subsectionIDModuleName, ignoredSubsectionSize, 5}, 265 expectedErr: "failed to read module name: EOF", 266 }, 267 { 268 name: "EOF after function name count", 269 input: []byte{subsectionIDFunctionNames, ignoredSubsectionSize, 2}, 270 expectedErr: "failed to read a function index in subsection[1]: EOF", 271 }, 272 { 273 name: "EOF after local names function count", 274 input: []byte{subsectionIDLocalNames, ignoredSubsectionSize, 2}, 275 expectedErr: "failed to read a function index in subsection[2]: EOF", 276 }, 277 { 278 name: "EOF after function name index", 279 input: []byte{subsectionIDFunctionNames, ignoredSubsectionSize, 2, 0}, 280 expectedErr: "failed to read function[0] name size: EOF", 281 }, 282 { 283 name: "EOF after local names function index", 284 input: []byte{subsectionIDLocalNames, ignoredSubsectionSize, 2, 0}, 285 expectedErr: "failed to read the local count for function[0]: EOF", 286 }, 287 { 288 name: "EOF after function name size", 289 input: []byte{subsectionIDFunctionNames, ignoredSubsectionSize, 2, 0, 5}, 290 expectedErr: "failed to read function[0] name: EOF", 291 }, 292 { 293 name: "EOF after local names count for a function index", 294 input: []byte{subsectionIDLocalNames, ignoredSubsectionSize, 2, 0, 2}, 295 expectedErr: "failed to read a local index of function[0]: EOF", 296 }, 297 { 298 name: "EOF after local name size", 299 input: []byte{subsectionIDLocalNames, ignoredSubsectionSize, 2, 0, 2, 1}, 300 expectedErr: "failed to read function[0] local[1] name size: EOF", 301 }, 302 } 303 304 for _, tt := range tests { 305 tc := tt 306 307 t.Run(tc.name, func(t *testing.T) { 308 _, err := decodeNameSection(bytes.NewReader(tc.input), uint64(len(tc.input))) 309 require.EqualError(t, err, tc.expectedErr) 310 }) 311 } 312 }