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