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