github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/wasm/binary/element_test.go (about) 1 package binary 2 3 import ( 4 "bytes" 5 "strconv" 6 "testing" 7 8 "github.com/wasilibs/wazerox/api" 9 "github.com/wasilibs/wazerox/internal/testing/require" 10 "github.com/wasilibs/wazerox/internal/wasm" 11 ) 12 13 func Test_ensureElementKindFuncRef(t *testing.T) { 14 require.NoError(t, ensureElementKindFuncRef(bytes.NewReader([]byte{0x0}))) 15 require.Error(t, ensureElementKindFuncRef(bytes.NewReader([]byte{0x1}))) 16 } 17 18 func Test_decodeElementInitValueVector(t *testing.T) { 19 tests := []struct { 20 in []byte 21 exp []wasm.Index 22 expErr string 23 }{ 24 { 25 in: []byte{0}, 26 exp: []wasm.Index{}, 27 }, 28 { 29 in: []byte{5, 1, 2, 3, 4, 5}, 30 exp: []wasm.Index{1, 2, 3, 4, 5}, 31 }, 32 { 33 in: []byte{ 34 1, 35 0xff, 0xff, 0xff, 0xff, 0xf, 36 }, 37 expErr: "too large function index in Element init: 4294967295", 38 }, 39 } 40 41 for i, tt := range tests { 42 tc := tt 43 t.Run(strconv.Itoa(i), func(t *testing.T) { 44 actual, err := decodeElementInitValueVector(bytes.NewReader(tc.in)) 45 if tc.expErr != "" { 46 require.EqualError(t, err, tc.expErr) 47 } else { 48 require.NoError(t, err) 49 require.Equal(t, tc.exp, actual) 50 } 51 }) 52 } 53 } 54 55 func Test_decodeElementConstExprVector(t *testing.T) { 56 tests := []struct { 57 in []byte 58 refType wasm.RefType 59 exp []wasm.Index 60 features api.CoreFeatures 61 }{ 62 { 63 in: []byte{0}, 64 exp: []wasm.Index{}, 65 refType: wasm.RefTypeFuncref, 66 features: api.CoreFeatureBulkMemoryOperations, 67 }, 68 { 69 in: []byte{ 70 2, // Two indexes. 71 wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd, 72 wasm.OpcodeRefFunc, 100, wasm.OpcodeEnd, 73 }, 74 exp: []wasm.Index{wasm.ElementInitNullReference, 100}, 75 refType: wasm.RefTypeFuncref, 76 features: api.CoreFeatureBulkMemoryOperations, 77 }, 78 { 79 in: []byte{ 80 4, // Three indexes. 81 wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd, 82 wasm.OpcodeRefFunc, 83 0x80, 0x7f, 84 wasm.OpcodeEnd, 85 wasm.OpcodeGlobalGet, 1, wasm.OpcodeEnd, 86 wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd, 87 }, 88 exp: []wasm.Index{ 89 wasm.ElementInitNullReference, 90 16256, 91 wasm.ElementInitImportedGlobalFunctionReference | 1, 92 wasm.ElementInitNullReference, 93 }, 94 refType: wasm.RefTypeFuncref, 95 features: api.CoreFeatureBulkMemoryOperations, 96 }, 97 { 98 in: []byte{ 99 2, // Two indexes. 100 wasm.OpcodeRefNull, wasm.RefTypeExternref, wasm.OpcodeEnd, 101 wasm.OpcodeRefNull, wasm.RefTypeExternref, wasm.OpcodeEnd, 102 }, 103 exp: []wasm.Index{wasm.ElementInitNullReference, wasm.ElementInitNullReference}, 104 refType: wasm.RefTypeExternref, 105 features: api.CoreFeatureBulkMemoryOperations, 106 }, 107 } 108 109 for i, tt := range tests { 110 tc := tt 111 t.Run(strconv.Itoa(i), func(t *testing.T) { 112 actual, err := decodeElementConstExprVector(bytes.NewReader(tc.in), tc.refType, tc.features) 113 require.NoError(t, err) 114 require.Equal(t, tc.exp, actual) 115 }) 116 } 117 } 118 119 func Test_decodeElementConstExprVector_errors(t *testing.T) { 120 tests := []struct { 121 name string 122 in []byte 123 refType wasm.RefType 124 expErr string 125 features api.CoreFeatures 126 }{ 127 { 128 name: "eof", 129 expErr: "failed to get the size of constexpr vector: EOF", 130 }, 131 { 132 name: "feature", 133 in: []byte{1, wasm.OpcodeRefNull, wasm.RefTypeExternref, wasm.OpcodeEnd}, 134 expErr: "ref.null is not supported as feature \"bulk-memory-operations\" is disabled", 135 }, 136 { 137 name: "type mismatch - ref.null", 138 in: []byte{1, wasm.OpcodeRefNull, wasm.RefTypeExternref, wasm.OpcodeEnd}, 139 refType: wasm.RefTypeFuncref, 140 features: api.CoreFeaturesV2, 141 expErr: "element type mismatch: want funcref, but constexpr has externref", 142 }, 143 { 144 name: "type mismatch - ref.null", 145 in: []byte{1, wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd}, 146 refType: wasm.RefTypeExternref, 147 features: api.CoreFeaturesV2, 148 expErr: "element type mismatch: want externref, but constexpr has funcref", 149 }, 150 { 151 name: "invalid ref type", 152 in: []byte{1, wasm.OpcodeRefNull, 0xff, wasm.OpcodeEnd}, 153 refType: wasm.RefTypeExternref, 154 features: api.CoreFeaturesV2, 155 expErr: "invalid type for ref.null: 0xff", 156 }, 157 { 158 name: "type mismatch - ref.fuc", 159 in: []byte{1, wasm.OpcodeRefFunc, 0, wasm.OpcodeEnd}, 160 refType: wasm.RefTypeExternref, 161 features: api.CoreFeaturesV2, 162 expErr: "element type mismatch: want externref, but constexpr has funcref", 163 }, 164 { 165 name: "too large index - ref.fuc", 166 in: []byte{1, wasm.OpcodeRefFunc, 0xff, 0xff, 0xff, 0xff, 0xf, wasm.OpcodeEnd}, 167 refType: wasm.RefTypeFuncref, 168 features: api.CoreFeaturesV2, 169 expErr: "too large function index in Element init: 4294967295", 170 }, 171 { 172 name: "type mismatch - global.get", 173 in: []byte{1, wasm.OpcodeGlobalGet, 0, wasm.OpcodeEnd}, 174 refType: wasm.RefTypeExternref, 175 features: api.CoreFeaturesV2, 176 expErr: "element type mismatch: want externref, but requires funcref", 177 }, 178 } 179 180 for _, tt := range tests { 181 tc := tt 182 t.Run(tc.name, func(t *testing.T) { 183 _, err := decodeElementConstExprVector(bytes.NewReader(tc.in), tc.refType, tc.features) 184 require.EqualError(t, err, tc.expErr) 185 }) 186 } 187 } 188 189 func TestDecodeElementSegment(t *testing.T) { 190 tests := []struct { 191 name string 192 in []byte 193 exp wasm.ElementSegment 194 expErr string 195 features api.CoreFeatures 196 }{ 197 { 198 name: "legacy", 199 in: []byte{ 200 0, // Prefix (which is previously the table index fixed to zero) 201 // Offset const expr. 202 wasm.OpcodeI32Const, 1, wasm.OpcodeEnd, 203 // Init vector. 204 5, 1, 2, 3, 4, 5, 205 }, 206 exp: wasm.ElementSegment{ 207 OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{1}}, 208 Init: []wasm.Index{1, 2, 3, 4, 5}, 209 Mode: wasm.ElementModeActive, 210 Type: wasm.RefTypeFuncref, 211 }, 212 features: api.CoreFeatureBulkMemoryOperations, 213 }, 214 { 215 name: "legacy multi byte const expr data", 216 in: []byte{ 217 0, // Prefix (which is previously the table index fixed to zero) 218 // Offset const expr. 219 wasm.OpcodeI32Const, 0x80, 0, wasm.OpcodeEnd, 220 // Init vector. 221 5, 1, 2, 3, 4, 5, 222 }, 223 exp: wasm.ElementSegment{ 224 OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 0}}, 225 Init: []wasm.Index{1, 2, 3, 4, 5}, 226 Mode: wasm.ElementModeActive, 227 Type: wasm.RefTypeFuncref, 228 }, 229 features: api.CoreFeatureBulkMemoryOperations, 230 }, 231 { 232 name: "passive value vector", 233 in: []byte{ 234 1, // Prefix. 235 0, // Elem kind must be fixed to zero. 236 // Init vector. 237 5, 1, 2, 3, 4, 5, 238 }, 239 exp: wasm.ElementSegment{ 240 Init: []wasm.Index{1, 2, 3, 4, 5}, 241 Mode: wasm.ElementModePassive, 242 Type: wasm.RefTypeFuncref, 243 }, 244 features: api.CoreFeatureBulkMemoryOperations, 245 }, 246 { 247 name: "active with table index encoded.", 248 in: []byte{ 249 2, // Prefix. 250 0, 251 // Offset const expr. 252 wasm.OpcodeI32Const, 0x80, 0, wasm.OpcodeEnd, 253 0, // Elem kind must be fixed to zero. 254 // Init vector. 255 5, 1, 2, 3, 4, 5, 256 }, 257 exp: wasm.ElementSegment{ 258 OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 0}}, 259 Init: []wasm.Index{1, 2, 3, 4, 5}, 260 Mode: wasm.ElementModeActive, 261 Type: wasm.RefTypeFuncref, 262 }, 263 features: api.CoreFeatureBulkMemoryOperations, 264 }, 265 { 266 name: "active with non zero table index encoded.", 267 in: []byte{ 268 2, // Prefix. 269 10, 270 // Offset const expr. 271 wasm.OpcodeI32Const, 0x80, 0, wasm.OpcodeEnd, 272 0, // Elem kind must be fixed to zero. 273 // Init vector. 274 5, 1, 2, 3, 4, 5, 275 }, 276 exp: wasm.ElementSegment{ 277 OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 0}}, 278 Init: []wasm.Index{1, 2, 3, 4, 5}, 279 Mode: wasm.ElementModeActive, 280 Type: wasm.RefTypeFuncref, 281 TableIndex: 10, 282 }, 283 features: api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes, 284 }, 285 { 286 name: "active with non zero table index encoded but reference-types disabled", 287 in: []byte{ 288 2, // Prefix. 289 10, 290 // Offset const expr. 291 wasm.OpcodeI32Const, 0x80, 0, wasm.OpcodeEnd, 292 0, // Elem kind must be fixed to zero. 293 // Init vector. 294 5, 1, 2, 3, 4, 5, 295 }, 296 expErr: `table index must be zero but was 10: feature "reference-types" is disabled`, 297 features: api.CoreFeatureBulkMemoryOperations, 298 }, 299 { 300 name: "declarative", 301 in: []byte{ 302 3, // Prefix. 303 0, // Elem kind must be fixed to zero. 304 // Init vector. 305 5, 1, 2, 3, 4, 5, 306 }, 307 exp: wasm.ElementSegment{ 308 Init: []wasm.Index{1, 2, 3, 4, 5}, 309 Mode: wasm.ElementModeDeclarative, 310 Type: wasm.RefTypeFuncref, 311 }, 312 features: api.CoreFeatureBulkMemoryOperations, 313 }, 314 { 315 name: "active const expr vector", 316 in: []byte{ 317 4, // Prefix. 318 // Offset expr. 319 wasm.OpcodeI32Const, 0x80, 1, wasm.OpcodeEnd, 320 // Init const expr vector. 321 3, // number of const expr. 322 wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd, 323 wasm.OpcodeRefFunc, 324 0x80, 0x7f, 325 wasm.OpcodeEnd, 326 wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd, 327 }, 328 exp: wasm.ElementSegment{ 329 OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 1}}, 330 Init: []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference}, 331 Mode: wasm.ElementModeActive, 332 Type: wasm.RefTypeFuncref, 333 }, 334 features: api.CoreFeatureBulkMemoryOperations, 335 }, 336 { 337 name: "passive const expr vector - funcref", 338 in: []byte{ 339 5, // Prefix. 340 wasm.RefTypeFuncref, 341 // Init const expr vector. 342 3, // number of const expr. 343 wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd, 344 wasm.OpcodeRefFunc, 345 0x80, 0x7f, 346 wasm.OpcodeEnd, 347 wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd, 348 }, 349 exp: wasm.ElementSegment{ 350 Init: []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference}, 351 Mode: wasm.ElementModePassive, 352 Type: wasm.RefTypeFuncref, 353 }, 354 features: api.CoreFeatureBulkMemoryOperations, 355 }, 356 { 357 name: "passive const expr vector - unknown ref type", 358 in: []byte{ 359 5, // Prefix. 360 0xff, 361 }, 362 expErr: `ref type must be funcref or externref for element as of WebAssembly 2.0`, 363 features: api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes, 364 }, 365 { 366 name: "active with table index and const expr vector", 367 in: []byte{ 368 6, // Prefix. 369 0, 370 // Offset expr. 371 wasm.OpcodeI32Const, 0x80, 1, wasm.OpcodeEnd, 372 wasm.RefTypeFuncref, 373 // Init const expr vector. 374 3, // number of const expr. 375 wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd, 376 wasm.OpcodeRefFunc, 377 0x80, 0x7f, 378 wasm.OpcodeEnd, 379 wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd, 380 }, 381 exp: wasm.ElementSegment{ 382 OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 1}}, 383 Init: []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference}, 384 Mode: wasm.ElementModeActive, 385 Type: wasm.RefTypeFuncref, 386 }, 387 features: api.CoreFeatureBulkMemoryOperations, 388 }, 389 { 390 name: "active with non zero table index and const expr vector", 391 in: []byte{ 392 6, // Prefix. 393 10, 394 // Offset expr. 395 wasm.OpcodeI32Const, 0x80, 1, wasm.OpcodeEnd, 396 wasm.RefTypeFuncref, 397 // Init const expr vector. 398 3, // number of const expr. 399 wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd, 400 wasm.OpcodeRefFunc, 401 0x80, 0x7f, 402 wasm.OpcodeEnd, 403 wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd, 404 }, 405 exp: wasm.ElementSegment{ 406 OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 1}}, 407 Init: []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference}, 408 Mode: wasm.ElementModeActive, 409 Type: wasm.RefTypeFuncref, 410 TableIndex: 10, 411 }, 412 features: api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes, 413 }, 414 { 415 name: "active with non zero table index and const expr vector but feature disabled", 416 in: []byte{ 417 6, // Prefix. 418 10, 419 // Offset expr. 420 wasm.OpcodeI32Const, 0x80, 1, wasm.OpcodeEnd, 421 wasm.RefTypeFuncref, 422 // Init const expr vector. 423 3, // number of const expr. 424 wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd, 425 wasm.OpcodeRefFunc, 426 0x80, 0x80, 0x80, 0x4f, // 165675008 in varint encoding. 427 wasm.OpcodeEnd, 428 wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd, 429 }, 430 expErr: `table index must be zero but was 10: feature "reference-types" is disabled`, 431 features: api.CoreFeatureBulkMemoryOperations, 432 }, 433 { 434 name: "declarative const expr vector", 435 in: []byte{ 436 7, // Prefix. 437 wasm.RefTypeFuncref, 438 // Init const expr vector. 439 2, // number of const expr. 440 wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd, 441 wasm.OpcodeRefFunc, 442 0x80, 0x7f, 443 wasm.OpcodeEnd, 444 }, 445 exp: wasm.ElementSegment{ 446 Init: []wasm.Index{wasm.ElementInitNullReference, 16256}, 447 Mode: wasm.ElementModeDeclarative, 448 Type: wasm.RefTypeFuncref, 449 }, 450 features: api.CoreFeatureBulkMemoryOperations, 451 }, 452 } 453 454 for _, tt := range tests { 455 tc := tt 456 t.Run(tc.name, func(t *testing.T) { 457 var actual wasm.ElementSegment 458 err := decodeElementSegment(bytes.NewReader(tc.in), tc.features, &actual) 459 if tc.expErr != "" { 460 require.EqualError(t, err, tc.expErr) 461 } else { 462 require.NoError(t, err) 463 require.Equal(t, actual, tc.exp) 464 } 465 }) 466 } 467 } 468 469 func TestDecodeElementSegment_errors(t *testing.T) { 470 var actual wasm.ElementSegment 471 err := decodeElementSegment(bytes.NewReader([]byte{1}), api.CoreFeatureMultiValue, &actual) 472 require.EqualError(t, err, `non-zero prefix for element segment is invalid as feature "bulk-memory-operations" is disabled`) 473 }