wa-lang.org/wazero@v1.0.2/internal/wasm/module_test.go (about) 1 package wasm 2 3 import ( 4 "fmt" 5 "math" 6 "testing" 7 8 "wa-lang.org/wazero/api" 9 "wa-lang.org/wazero/internal/leb128" 10 "wa-lang.org/wazero/internal/testing/require" 11 "wa-lang.org/wazero/internal/u64" 12 ) 13 14 func TestFunctionType_String(t *testing.T) { 15 tests := []struct { 16 functype *FunctionType 17 exp string 18 }{ 19 {functype: &FunctionType{}, exp: "v_v"}, 20 {functype: &FunctionType{Params: []ValueType{ValueTypeI32}}, exp: "i32_v"}, 21 {functype: &FunctionType{Params: []ValueType{ValueTypeI32, ValueTypeF64}}, exp: "i32f64_v"}, 22 {functype: &FunctionType{Params: []ValueType{ValueTypeF32, ValueTypeI32, ValueTypeF64}}, exp: "f32i32f64_v"}, 23 {functype: &FunctionType{Results: []ValueType{ValueTypeI64}}, exp: "v_i64"}, 24 {functype: &FunctionType{Results: []ValueType{ValueTypeI64, ValueTypeF32}}, exp: "v_i64f32"}, 25 {functype: &FunctionType{Results: []ValueType{ValueTypeF32, ValueTypeI32, ValueTypeF64}}, exp: "v_f32i32f64"}, 26 {functype: &FunctionType{Params: []ValueType{ValueTypeI32}, Results: []ValueType{ValueTypeI64}}, exp: "i32_i64"}, 27 {functype: &FunctionType{Params: []ValueType{ValueTypeI64, ValueTypeF32}, Results: []ValueType{ValueTypeI64, ValueTypeF32}}, exp: "i64f32_i64f32"}, 28 {functype: &FunctionType{Params: []ValueType{ValueTypeI64, ValueTypeF32, ValueTypeF64}, Results: []ValueType{ValueTypeF32, ValueTypeI32, ValueTypeF64}}, exp: "i64f32f64_f32i32f64"}, 29 } 30 31 for _, tt := range tests { 32 tc := tt 33 t.Run(tc.functype.String(), func(t *testing.T) { 34 require.Equal(t, tc.exp, tc.functype.String()) 35 require.Equal(t, tc.exp, tc.functype.key()) 36 require.Equal(t, tc.exp, tc.functype.string) 37 }) 38 } 39 } 40 41 func TestSectionIDName(t *testing.T) { 42 tests := []struct { 43 name string 44 input SectionID 45 expected string 46 }{ 47 {"custom", SectionIDCustom, "custom"}, 48 {"type", SectionIDType, "type"}, 49 {"import", SectionIDImport, "import"}, 50 {"function", SectionIDFunction, "function"}, 51 {"table", SectionIDTable, "table"}, 52 {"memory", SectionIDMemory, "memory"}, 53 {"global", SectionIDGlobal, "global"}, 54 {"export", SectionIDExport, "export"}, 55 {"start", SectionIDStart, "start"}, 56 {"element", SectionIDElement, "element"}, 57 {"code", SectionIDCode, "code"}, 58 {"data", SectionIDData, "data"}, 59 {"unknown", 100, "unknown"}, 60 } 61 62 for _, tt := range tests { 63 tc := tt 64 65 t.Run(tc.name, func(t *testing.T) { 66 require.Equal(t, tc.expected, SectionIDName(tc.input)) 67 }) 68 } 69 } 70 71 func TestMemory_Validate(t *testing.T) { 72 tests := []struct { 73 name string 74 mem *Memory 75 expectedErr string 76 }{ 77 { 78 name: "ok", 79 mem: &Memory{Min: 2, Cap: 2, Max: 2}, 80 }, 81 { 82 name: "cap < min", 83 mem: &Memory{Min: 2, Cap: 1, Max: 2}, 84 expectedErr: "capacity 1 pages (64 Ki) less than minimum 2 pages (128 Ki)", 85 }, 86 { 87 name: "cap > maxLimit", 88 mem: &Memory{Min: 2, Cap: math.MaxUint32, Max: 2}, 89 expectedErr: "capacity 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)", 90 }, 91 { 92 name: "max < min", 93 mem: &Memory{Min: 2, Cap: 2, Max: 0, IsMaxEncoded: true}, 94 expectedErr: "min 2 pages (128 Ki) > max 0 pages (0 Ki)", 95 }, 96 { 97 name: "min > limit", 98 mem: &Memory{Min: math.MaxUint32}, 99 expectedErr: "min 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)", 100 }, 101 { 102 name: "max > limit", 103 mem: &Memory{Max: math.MaxUint32, IsMaxEncoded: true}, 104 expectedErr: "max 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)", 105 }, 106 } 107 108 for _, tt := range tests { 109 tc := tt 110 111 t.Run(tc.name, func(t *testing.T) { 112 err := tc.mem.Validate(MemoryLimitPages) 113 if tc.expectedErr == "" { 114 require.NoError(t, err) 115 } else { 116 require.EqualError(t, err, tc.expectedErr) 117 } 118 }) 119 } 120 } 121 122 func TestModule_allDeclarations(t *testing.T) { 123 tests := []struct { 124 module *Module 125 expectedFunctions []Index 126 expectedGlobals []*GlobalType 127 expectedMemory *Memory 128 expectedTables []*Table 129 }{ 130 // Functions. 131 { 132 module: &Module{ 133 ImportSection: []*Import{{Type: ExternTypeFunc, DescFunc: 10000}}, 134 FunctionSection: []Index{10, 20, 30}, 135 }, 136 expectedFunctions: []Index{10000, 10, 20, 30}, 137 }, 138 { 139 module: &Module{ 140 FunctionSection: []Index{10, 20, 30}, 141 }, 142 expectedFunctions: []Index{10, 20, 30}, 143 }, 144 { 145 module: &Module{ 146 ImportSection: []*Import{{Type: ExternTypeFunc, DescFunc: 10000}}, 147 }, 148 expectedFunctions: []Index{10000}, 149 }, 150 // Globals. 151 { 152 module: &Module{ 153 ImportSection: []*Import{{Type: ExternTypeGlobal, DescGlobal: &GlobalType{Mutable: false}}}, 154 GlobalSection: []*Global{{Type: &GlobalType{Mutable: true}}}, 155 }, 156 expectedGlobals: []*GlobalType{{Mutable: false}, {Mutable: true}}, 157 }, 158 { 159 module: &Module{ 160 GlobalSection: []*Global{{Type: &GlobalType{Mutable: true}}}, 161 }, 162 expectedGlobals: []*GlobalType{{Mutable: true}}, 163 }, 164 { 165 module: &Module{ 166 ImportSection: []*Import{{Type: ExternTypeGlobal, DescGlobal: &GlobalType{Mutable: false}}}, 167 }, 168 expectedGlobals: []*GlobalType{{Mutable: false}}, 169 }, 170 // Memories. 171 { 172 module: &Module{ 173 ImportSection: []*Import{{Type: ExternTypeMemory, DescMem: &Memory{Min: 1, Max: 10}}}, 174 }, 175 expectedMemory: &Memory{Min: 1, Max: 10}, 176 }, 177 { 178 module: &Module{ 179 MemorySection: &Memory{Min: 100}, 180 }, 181 expectedMemory: &Memory{Min: 100}, 182 }, 183 // Tables. 184 { 185 module: &Module{ 186 ImportSection: []*Import{{Type: ExternTypeTable, DescTable: &Table{Min: 1}}}, 187 }, 188 expectedTables: []*Table{{Min: 1}}, 189 }, 190 { 191 module: &Module{ 192 TableSection: []*Table{{Min: 10}}, 193 }, 194 expectedTables: []*Table{{Min: 10}}, 195 }, 196 } 197 198 for i, tt := range tests { 199 tc := tt 200 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 201 functions, globals, memory, tables, err := tc.module.AllDeclarations() 202 require.NoError(t, err) 203 require.Equal(t, tc.expectedFunctions, functions) 204 require.Equal(t, tc.expectedGlobals, globals) 205 require.Equal(t, tc.expectedTables, tables) 206 require.Equal(t, tc.expectedMemory, memory) 207 }) 208 } 209 } 210 211 func TestValidateConstExpression(t *testing.T) { 212 t.Run("invalid opcode", func(t *testing.T) { 213 expr := &ConstantExpression{Opcode: OpcodeNop} 214 err := validateConstExpression(nil, 0, expr, valueTypeUnknown) 215 require.Error(t, err) 216 }) 217 for _, vt := range []ValueType{ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64} { 218 t.Run(ValueTypeName(vt), func(t *testing.T) { 219 t.Run("valid", func(t *testing.T) { 220 expr := &ConstantExpression{} 221 switch vt { 222 case ValueTypeI32: 223 expr.Data = []byte{1} 224 expr.Opcode = OpcodeI32Const 225 case ValueTypeI64: 226 expr.Data = []byte{2} 227 expr.Opcode = OpcodeI64Const 228 case ValueTypeF32: 229 expr.Data = u64.LeBytes(api.EncodeF32(math.MaxFloat32)) 230 expr.Opcode = OpcodeF32Const 231 case ValueTypeF64: 232 expr.Data = u64.LeBytes(api.EncodeF64(math.MaxFloat64)) 233 expr.Opcode = OpcodeF64Const 234 } 235 236 err := validateConstExpression(nil, 0, expr, vt) 237 require.NoError(t, err) 238 }) 239 t.Run("invalid", func(t *testing.T) { 240 // Empty data must be failure. 241 expr := &ConstantExpression{Data: make([]byte, 0)} 242 switch vt { 243 case ValueTypeI32: 244 expr.Opcode = OpcodeI32Const 245 case ValueTypeI64: 246 expr.Opcode = OpcodeI64Const 247 case ValueTypeF32: 248 expr.Opcode = OpcodeF32Const 249 case ValueTypeF64: 250 expr.Opcode = OpcodeF64Const 251 } 252 err := validateConstExpression(nil, 0, expr, vt) 253 require.Error(t, err) 254 }) 255 }) 256 } 257 t.Run("ref types", func(t *testing.T) { 258 t.Run("ref.func", func(t *testing.T) { 259 expr := &ConstantExpression{Data: []byte{5}, Opcode: OpcodeRefFunc} 260 err := validateConstExpression(nil, 10, expr, ValueTypeFuncref) 261 require.NoError(t, err) 262 err = validateConstExpression(nil, 2, expr, ValueTypeFuncref) 263 require.EqualError(t, err, "ref.func index out of range [5] with length 1") 264 }) 265 t.Run("ref.null", func(t *testing.T) { 266 err := validateConstExpression(nil, 0, 267 &ConstantExpression{Data: []byte{ValueTypeFuncref}, Opcode: OpcodeRefNull}, 268 ValueTypeFuncref) 269 require.NoError(t, err) 270 err = validateConstExpression(nil, 0, 271 &ConstantExpression{Data: []byte{ValueTypeExternref}, Opcode: OpcodeRefNull}, 272 ValueTypeExternref) 273 require.NoError(t, err) 274 err = validateConstExpression(nil, 0, 275 &ConstantExpression{Data: []byte{0xff}, Opcode: OpcodeRefNull}, 276 ValueTypeExternref) 277 require.EqualError(t, err, "invalid type for ref.null: 0xff") 278 }) 279 }) 280 t.Run("global expr", func(t *testing.T) { 281 t.Run("failed to read global index", func(t *testing.T) { 282 // Empty data for global index is invalid. 283 expr := &ConstantExpression{Data: make([]byte, 0), Opcode: OpcodeGlobalGet} 284 err := validateConstExpression(nil, 0, expr, valueTypeUnknown) 285 require.Error(t, err) 286 }) 287 t.Run("global index out of range", func(t *testing.T) { 288 // Data holds the index in leb128 and this time the value exceeds len(globals) (=0). 289 expr := &ConstantExpression{Data: []byte{1}, Opcode: OpcodeGlobalGet} 290 var globals []*GlobalType 291 err := validateConstExpression(globals, 0, expr, valueTypeUnknown) 292 require.Error(t, err) 293 }) 294 295 t.Run("type mismatch", func(t *testing.T) { 296 for _, vt := range []ValueType{ 297 ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64, 298 } { 299 t.Run(ValueTypeName(vt), func(t *testing.T) { 300 // The index specified in Data equals zero. 301 expr := &ConstantExpression{Data: []byte{0}, Opcode: OpcodeGlobalGet} 302 globals := []*GlobalType{{ValType: valueTypeUnknown}} 303 304 err := validateConstExpression(globals, 0, expr, vt) 305 require.Error(t, err) 306 }) 307 } 308 }) 309 t.Run("ok", func(t *testing.T) { 310 for _, vt := range []ValueType{ 311 ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64, 312 } { 313 t.Run(ValueTypeName(vt), func(t *testing.T) { 314 // The index specified in Data equals zero. 315 expr := &ConstantExpression{Data: []byte{0}, Opcode: OpcodeGlobalGet} 316 globals := []*GlobalType{{ValType: vt}} 317 318 err := validateConstExpression(globals, 0, expr, vt) 319 require.NoError(t, err) 320 }) 321 } 322 }) 323 }) 324 } 325 326 func TestModule_Validate_Errors(t *testing.T) { 327 zero := Index(0) 328 tests := []struct { 329 name string 330 input *Module 331 expectedErr string 332 }{ 333 { 334 name: "StartSection points to an invalid func", 335 input: &Module{ 336 TypeSection: nil, 337 FunctionSection: []uint32{0}, 338 CodeSection: []*Code{{Body: []byte{OpcodeEnd}}}, 339 StartSection: &zero, 340 }, 341 expectedErr: "invalid start function: func[0] has an invalid type", 342 }, 343 } 344 345 for _, tt := range tests { 346 tc := tt 347 348 t.Run(tc.name, func(t *testing.T) { 349 err := tc.input.Validate(api.CoreFeaturesV1) 350 require.EqualError(t, err, tc.expectedErr) 351 }) 352 } 353 } 354 355 func TestModule_validateStartSection(t *testing.T) { 356 t.Run("no start section", func(t *testing.T) { 357 m := Module{} 358 err := m.validateStartSection() 359 require.NoError(t, err) 360 }) 361 362 t.Run("invalid type", func(t *testing.T) { 363 for _, ft := range []*FunctionType{ 364 {Params: []ValueType{ValueTypeI32}}, 365 {Results: []ValueType{ValueTypeI32}}, 366 {Params: []ValueType{ValueTypeI32}, Results: []ValueType{ValueTypeI32}}, 367 } { 368 t.Run(ft.String(), func(t *testing.T) { 369 index := uint32(0) 370 m := Module{StartSection: &index, FunctionSection: []uint32{0}, TypeSection: []*FunctionType{ft}} 371 err := m.validateStartSection() 372 require.Error(t, err) 373 }) 374 } 375 }) 376 t.Run("imported valid func", func(t *testing.T) { 377 index := Index(1) 378 m := Module{ 379 StartSection: &index, 380 TypeSection: []*FunctionType{{}, {Results: []ValueType{ValueTypeI32}}}, 381 ImportSection: []*Import{ 382 {Type: ExternTypeFunc, DescFunc: 1}, 383 // import with index 1 is global but this should be skipped when searching imported functions. 384 {Type: ExternTypeGlobal}, 385 {Type: ExternTypeFunc, DescFunc: 0}, // This one must be selected. 386 }, 387 } 388 err := m.validateStartSection() 389 require.NoError(t, err) 390 }) 391 } 392 393 func TestModule_validateGlobals(t *testing.T) { 394 t.Run("too many globals", func(t *testing.T) { 395 m := Module{} 396 err := m.validateGlobals(make([]*GlobalType, 10), 0, 9) 397 require.Error(t, err) 398 require.EqualError(t, err, "too many globals in a module") 399 }) 400 t.Run("global index out of range", func(t *testing.T) { 401 m := Module{GlobalSection: []*Global{ 402 { 403 Type: &GlobalType{ValType: ValueTypeI32}, 404 // Trying to reference globals[1] which is not imported. 405 Init: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{1}}, 406 }, 407 }} 408 err := m.validateGlobals(nil, 0, 9) 409 require.Error(t, err) 410 require.EqualError(t, err, "global index out of range") 411 }) 412 t.Run("invalid const expression", func(t *testing.T) { 413 m := Module{GlobalSection: []*Global{ 414 { 415 Type: &GlobalType{ValType: valueTypeUnknown}, 416 Init: &ConstantExpression{Opcode: OpcodeUnreachable}, 417 }, 418 }} 419 err := m.validateGlobals(nil, 0, 9) 420 require.Error(t, err) 421 require.EqualError(t, err, "invalid opcode for const expression: 0x0") 422 }) 423 t.Run("ok", func(t *testing.T) { 424 m := Module{GlobalSection: []*Global{ 425 { 426 Type: &GlobalType{ValType: ValueTypeI32}, 427 Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0}, 428 }, 429 }} 430 err := m.validateGlobals(nil, 0, 9) 431 require.NoError(t, err) 432 }) 433 t.Run("ok with imported global", func(t *testing.T) { 434 m := Module{ 435 GlobalSection: []*Global{ 436 { 437 Type: &GlobalType{ValType: ValueTypeI32}, 438 // Trying to reference globals[1] which is imported. 439 Init: &ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0}}, 440 }, 441 }, 442 ImportSection: []*Import{{Type: ExternTypeGlobal}}, 443 } 444 globalDeclarations := []*GlobalType{ 445 {ValType: ValueTypeI32}, // Imported one. 446 nil, // the local one trying to validate. 447 } 448 err := m.validateGlobals(globalDeclarations, 0, 9) 449 require.NoError(t, err) 450 }) 451 } 452 453 func TestModule_validateFunctions(t *testing.T) { 454 t.Run("ok", func(t *testing.T) { 455 m := Module{ 456 TypeSection: []*FunctionType{v_v}, 457 FunctionSection: []uint32{0}, 458 CodeSection: []*Code{{Body: []byte{OpcodeI32Const, 0, OpcodeDrop, OpcodeEnd}}}, 459 } 460 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex) 461 require.NoError(t, err) 462 }) 463 t.Run("too many functions", func(t *testing.T) { 464 m := Module{} 465 err := m.validateFunctions(api.CoreFeaturesV1, []uint32{1, 2, 3, 4}, nil, nil, nil, 3) 466 require.Error(t, err) 467 require.EqualError(t, err, "too many functions in a store") 468 }) 469 t.Run("function, but no code", func(t *testing.T) { 470 m := Module{ 471 TypeSection: []*FunctionType{v_v}, 472 FunctionSection: []Index{0}, 473 CodeSection: nil, 474 } 475 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex) 476 require.Error(t, err) 477 require.EqualError(t, err, "code count (0) != function count (1)") 478 }) 479 t.Run("function out of range of code", func(t *testing.T) { 480 m := Module{ 481 TypeSection: []*FunctionType{v_v}, 482 FunctionSection: []Index{1}, 483 CodeSection: []*Code{{Body: []byte{OpcodeEnd}}}, 484 } 485 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex) 486 require.Error(t, err) 487 require.EqualError(t, err, "invalid function[0]: type section index 1 out of range") 488 }) 489 t.Run("invalid", func(t *testing.T) { 490 m := Module{ 491 TypeSection: []*FunctionType{v_v}, 492 FunctionSection: []Index{0}, 493 CodeSection: []*Code{{Body: []byte{OpcodeF32Abs}}}, 494 } 495 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex) 496 require.Error(t, err) 497 require.Contains(t, err.Error(), "invalid function[0]: cannot pop the 1st f32 operand") 498 }) 499 t.Run("in- exported", func(t *testing.T) { 500 m := Module{ 501 TypeSection: []*FunctionType{v_v}, 502 FunctionSection: []Index{0}, 503 CodeSection: []*Code{{Body: []byte{OpcodeF32Abs}}}, 504 ExportSection: []*Export{{Name: "f1", Type: ExternTypeFunc, Index: 0}}, 505 } 506 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex) 507 require.Error(t, err) 508 require.Contains(t, err.Error(), `invalid function[0] export["f1"]: cannot pop the 1st f32`) 509 }) 510 t.Run("in- exported after import", func(t *testing.T) { 511 m := Module{ 512 TypeSection: []*FunctionType{v_v}, 513 ImportSection: []*Import{{Type: ExternTypeFunc}}, 514 FunctionSection: []Index{0}, 515 CodeSection: []*Code{{Body: []byte{OpcodeF32Abs}}}, 516 ExportSection: []*Export{{Name: "f1", Type: ExternTypeFunc, Index: 1}}, 517 } 518 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex) 519 require.Error(t, err) 520 require.Contains(t, err.Error(), `invalid function[0] export["f1"]: cannot pop the 1st f32`) 521 }) 522 t.Run("in- exported twice", func(t *testing.T) { 523 m := Module{ 524 TypeSection: []*FunctionType{v_v}, 525 FunctionSection: []Index{0}, 526 CodeSection: []*Code{{Body: []byte{OpcodeF32Abs}}}, 527 ExportSection: []*Export{ 528 {Name: "f1", Type: ExternTypeFunc, Index: 0}, 529 {Name: "f2", Type: ExternTypeFunc, Index: 0}, 530 }, 531 } 532 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex) 533 require.Error(t, err) 534 require.Contains(t, err.Error(), `invalid function[0] export["f1","f2"]: cannot pop the 1st f32`) 535 }) 536 } 537 538 func TestModule_validateMemory(t *testing.T) { 539 t.Run("active data segment exits but memory not declared", func(t *testing.T) { 540 m := Module{DataSection: []*DataSegment{{OffsetExpression: &ConstantExpression{}}}} 541 err := m.validateMemory(nil, nil, api.CoreFeaturesV1) 542 require.Error(t, err) 543 require.Contains(t, "unknown memory", err.Error()) 544 }) 545 t.Run("invalid const expr", func(t *testing.T) { 546 m := Module{DataSection: []*DataSegment{{ 547 OffsetExpression: &ConstantExpression{ 548 Opcode: OpcodeUnreachable, // Invalid! 549 }, 550 }}} 551 err := m.validateMemory(&Memory{}, nil, api.CoreFeaturesV1) 552 require.EqualError(t, err, "calculate offset: invalid opcode for const expression: 0x0") 553 }) 554 t.Run("ok", func(t *testing.T) { 555 m := Module{DataSection: []*DataSegment{{ 556 Init: []byte{0x1}, 557 OffsetExpression: &ConstantExpression{ 558 Opcode: OpcodeI32Const, 559 Data: leb128.EncodeInt32(1), 560 }, 561 }}} 562 err := m.validateMemory(&Memory{}, nil, api.CoreFeaturesV1) 563 require.NoError(t, err) 564 }) 565 } 566 567 func TestModule_validateImports(t *testing.T) { 568 tests := []struct { 569 name string 570 enabledFeatures api.CoreFeatures 571 i *Import 572 expectedErr string 573 }{ 574 {name: "empty import section"}, 575 { 576 name: "func", 577 enabledFeatures: api.CoreFeaturesV1, 578 i: &Import{Module: "m", Name: "n", Type: ExternTypeFunc, DescFunc: 0}, 579 }, 580 { 581 name: "global var disabled", 582 enabledFeatures: api.CoreFeaturesV1.SetEnabled(api.CoreFeatureMutableGlobal, false), 583 i: &Import{ 584 Module: "m", 585 Name: "n", 586 Type: ExternTypeGlobal, 587 DescGlobal: &GlobalType{ValType: ValueTypeI32, Mutable: true}, 588 }, 589 expectedErr: `invalid import["m"."n"] global: feature "mutable-global" is disabled`, 590 }, 591 { 592 name: "table", 593 enabledFeatures: api.CoreFeaturesV1, 594 i: &Import{ 595 Module: "m", 596 Name: "n", 597 Type: ExternTypeTable, 598 DescTable: &Table{Min: 1}, 599 }, 600 }, 601 { 602 name: "memory", 603 enabledFeatures: api.CoreFeaturesV1, 604 i: &Import{ 605 Module: "m", 606 Name: "n", 607 Type: ExternTypeMemory, 608 DescMem: &Memory{Min: 1}, 609 }, 610 }, 611 } 612 613 for _, tt := range tests { 614 tc := tt 615 t.Run(tc.name, func(t *testing.T) { 616 m := Module{} 617 if tc.i != nil { 618 m.ImportSection = []*Import{tc.i} 619 } 620 err := m.validateImports(tc.enabledFeatures) 621 if tc.expectedErr != "" { 622 require.EqualError(t, err, tc.expectedErr) 623 } else { 624 require.NoError(t, err) 625 } 626 }) 627 } 628 } 629 630 func TestModule_validateExports(t *testing.T) { 631 tests := []struct { 632 name string 633 enabledFeatures api.CoreFeatures 634 exportSection []*Export 635 functions []Index 636 globals []*GlobalType 637 memory *Memory 638 tables []*Table 639 expectedErr string 640 }{ 641 {name: "empty export section", exportSection: []*Export{}}, 642 { 643 name: "func", 644 enabledFeatures: api.CoreFeaturesV1, 645 exportSection: []*Export{{Type: ExternTypeFunc, Index: 0}}, 646 functions: []Index{100 /* arbitrary type id*/}, 647 }, 648 { 649 name: "func out of range", 650 enabledFeatures: api.CoreFeaturesV1, 651 exportSection: []*Export{{Type: ExternTypeFunc, Index: 1, Name: "e"}}, 652 functions: []Index{100 /* arbitrary type id*/}, 653 expectedErr: `unknown function for export["e"]`, 654 }, 655 { 656 name: "global const", 657 enabledFeatures: api.CoreFeaturesV1, 658 exportSection: []*Export{{Type: ExternTypeGlobal, Index: 0}}, 659 globals: []*GlobalType{{ValType: ValueTypeI32}}, 660 }, 661 { 662 name: "global var", 663 enabledFeatures: api.CoreFeaturesV1, 664 exportSection: []*Export{{Type: ExternTypeGlobal, Index: 0}}, 665 globals: []*GlobalType{{ValType: ValueTypeI32, Mutable: true}}, 666 }, 667 { 668 name: "global var disabled", 669 enabledFeatures: api.CoreFeaturesV1.SetEnabled(api.CoreFeatureMutableGlobal, false), 670 exportSection: []*Export{{Type: ExternTypeGlobal, Index: 0, Name: "e"}}, 671 globals: []*GlobalType{{ValType: ValueTypeI32, Mutable: true}}, 672 expectedErr: `invalid export["e"] global[0]: feature "mutable-global" is disabled`, 673 }, 674 { 675 name: "global out of range", 676 enabledFeatures: api.CoreFeaturesV1, 677 exportSection: []*Export{{Type: ExternTypeGlobal, Index: 1, Name: "e"}}, 678 globals: []*GlobalType{{}}, 679 expectedErr: `unknown global for export["e"]`, 680 }, 681 { 682 name: "table", 683 enabledFeatures: api.CoreFeaturesV1, 684 exportSection: []*Export{{Type: ExternTypeTable, Index: 0}}, 685 tables: []*Table{{}}, 686 }, 687 { 688 name: "multiple tables", 689 enabledFeatures: api.CoreFeaturesV1, 690 exportSection: []*Export{{Type: ExternTypeTable, Index: 0}, {Type: ExternTypeTable, Index: 1}, {Type: ExternTypeTable, Index: 2}}, 691 tables: []*Table{{}, {}, {}}, 692 }, 693 { 694 name: "table out of range", 695 enabledFeatures: api.CoreFeaturesV1, 696 exportSection: []*Export{{Type: ExternTypeTable, Index: 1, Name: "e"}}, 697 tables: []*Table{}, 698 expectedErr: `table for export["e"] out of range`, 699 }, 700 { 701 name: "memory", 702 enabledFeatures: api.CoreFeaturesV1, 703 exportSection: []*Export{{Type: ExternTypeMemory, Index: 0}}, 704 memory: &Memory{}, 705 }, 706 { 707 name: "memory out of range", 708 enabledFeatures: api.CoreFeaturesV1, 709 exportSection: []*Export{{Type: ExternTypeMemory, Index: 0, Name: "e"}}, 710 tables: []*Table{}, 711 expectedErr: `memory for export["e"] out of range`, 712 }, 713 } 714 715 for _, tt := range tests { 716 tc := tt 717 t.Run(tc.name, func(t *testing.T) { 718 m := Module{ExportSection: tc.exportSection} 719 err := m.validateExports(tc.enabledFeatures, tc.functions, tc.globals, tc.memory, tc.tables) 720 if tc.expectedErr != "" { 721 require.EqualError(t, err, tc.expectedErr) 722 } else { 723 require.NoError(t, err) 724 } 725 }) 726 } 727 } 728 729 func TestModule_buildGlobals(t *testing.T) { 730 minusOne := int32(-1) 731 m := Module{GlobalSection: []*Global{ 732 { 733 Type: &GlobalType{Mutable: true, ValType: ValueTypeF64}, 734 Init: &ConstantExpression{ 735 Opcode: OpcodeF64Const, 736 Data: u64.LeBytes(api.EncodeF64(math.MaxFloat64)), 737 }, 738 }, 739 { 740 Type: &GlobalType{Mutable: false, ValType: ValueTypeI32}, 741 Init: &ConstantExpression{ 742 Opcode: OpcodeI32Const, 743 Data: leb128.EncodeInt32(math.MaxInt32), 744 }, 745 }, 746 { 747 Type: &GlobalType{Mutable: false, ValType: ValueTypeI32}, 748 Init: &ConstantExpression{ 749 Opcode: OpcodeI32Const, 750 Data: leb128.EncodeInt32(minusOne), 751 }, 752 }, 753 { 754 Type: &GlobalType{Mutable: false, ValType: ValueTypeV128}, 755 Init: &ConstantExpression{ 756 Opcode: OpcodeVecV128Const, 757 Data: []byte{ 758 1, 0, 0, 0, 0, 0, 0, 0, 759 2, 0, 0, 0, 0, 0, 0, 0, 760 }, 761 }, 762 }, 763 }} 764 765 globals := m.buildGlobals(nil) 766 expectedGlobals := []*GlobalInstance{ 767 {Type: &GlobalType{ValType: ValueTypeF64, Mutable: true}, Val: api.EncodeF64(math.MaxFloat64)}, 768 {Type: &GlobalType{ValType: ValueTypeI32, Mutable: false}, Val: uint64(int32(math.MaxInt32))}, 769 // Higher bits are must be zeroed for i32 globals, not signed-extended. See #656. 770 {Type: &GlobalType{ValType: ValueTypeI32, Mutable: false}, Val: uint64(uint32(minusOne))}, 771 {Type: &GlobalType{ValType: ValueTypeV128, Mutable: false}, Val: 0x1, ValHi: 0x2}, 772 } 773 require.Equal(t, expectedGlobals, globals) 774 } 775 776 func TestModule_buildFunctions(t *testing.T) { 777 nopCode := &Code{Body: []byte{OpcodeEnd}} 778 m := &Module{ 779 TypeSection: []*FunctionType{v_v}, 780 ImportSection: []*Import{{Type: ExternTypeFunc}}, 781 FunctionSection: []Index{0, 0, 0, 0, 0}, 782 CodeSection: []*Code{nopCode, nopCode, nopCode, nopCode, nopCode}, 783 FunctionDefinitionSection: []*FunctionDefinition{ 784 {index: 0, funcType: v_v}, 785 {index: 1, funcType: v_v}, 786 {index: 2, funcType: v_v, name: "two"}, 787 {index: 3, funcType: v_v}, 788 {index: 4, funcType: v_v, name: "four"}, 789 {index: 5, funcType: v_v, name: "five"}, 790 }, 791 } 792 793 // Note: This only returns module-defined functions, not imported ones. That's why the index starts with 1, not 0. 794 instance := &ModuleInstance{Name: "counter", TypeIDs: []FunctionTypeID{0}} 795 instance.BuildFunctions(m) 796 for i, f := range instance.Functions { 797 require.Equal(t, i, f.Definition.Index()) 798 require.Equal(t, nopCode.Body, f.Body) 799 } 800 } 801 802 func TestModule_buildMemoryInstance(t *testing.T) { 803 t.Run("nil", func(t *testing.T) { 804 m := Module{} 805 mem := m.buildMemory() 806 require.Nil(t, mem) 807 }) 808 t.Run("non-nil", func(t *testing.T) { 809 min := uint32(1) 810 max := uint32(10) 811 m := Module{MemorySection: &Memory{Min: min, Cap: min, Max: max}} 812 mem := m.buildMemory() 813 require.Equal(t, min, mem.Min) 814 require.Equal(t, max, mem.Max) 815 }) 816 } 817 818 func TestModule_validateDataCountSection(t *testing.T) { 819 t.Run("ok", func(t *testing.T) { 820 for _, m := range []*Module{ 821 { 822 DataSection: []*DataSegment{}, 823 DataCountSection: nil, 824 }, 825 { 826 DataSection: []*DataSegment{{}, {}}, 827 DataCountSection: nil, 828 }, 829 } { 830 err := m.validateDataCountSection() 831 require.NoError(t, err) 832 } 833 }) 834 t.Run("error", func(t *testing.T) { 835 count := uint32(1) 836 for _, m := range []*Module{ 837 { 838 DataSection: []*DataSegment{}, 839 DataCountSection: &count, 840 }, 841 { 842 DataSection: []*DataSegment{{}, {}}, 843 DataCountSection: &count, 844 }, 845 } { 846 err := m.validateDataCountSection() 847 require.Error(t, err) 848 } 849 }) 850 } 851 852 func TestModule_declaredFunctionIndexes(t *testing.T) { 853 tests := []struct { 854 name string 855 mod *Module 856 exp map[Index]struct{} 857 expErr string 858 }{ 859 { 860 name: "empty", 861 mod: &Module{}, 862 exp: map[uint32]struct{}{}, 863 }, 864 { 865 name: "global", 866 mod: &Module{ 867 ExportSection: []*Export{ 868 {Index: 10, Type: ExternTypeFunc}, 869 {Index: 1000, Type: ExternTypeGlobal}, 870 }, 871 }, 872 exp: map[uint32]struct{}{10: {}}, 873 }, 874 { 875 name: "export", 876 mod: &Module{ 877 ExportSection: []*Export{ 878 {Index: 1000, Type: ExternTypeGlobal}, 879 {Index: 10, Type: ExternTypeFunc}, 880 }, 881 }, 882 exp: map[uint32]struct{}{10: {}}, 883 }, 884 { 885 name: "element", 886 mod: &Module{ 887 ElementSection: []*ElementSegment{ 888 { 889 Mode: ElementModeActive, 890 Init: []*Index{uint32Ptr(0), nil, uint32Ptr(5)}, 891 }, 892 { 893 Mode: ElementModeDeclarative, 894 Init: []*Index{uint32Ptr(1), nil, uint32Ptr(5)}, 895 }, 896 { 897 Mode: ElementModePassive, 898 Init: []*Index{uint32Ptr(5), uint32Ptr(2), nil, nil}, 899 }, 900 }, 901 }, 902 exp: map[uint32]struct{}{0: {}, 1: {}, 2: {}, 5: {}}, 903 }, 904 { 905 name: "all", 906 mod: &Module{ 907 ExportSection: []*Export{ 908 {Index: 10, Type: ExternTypeGlobal}, 909 {Index: 1000, Type: ExternTypeFunc}, 910 }, 911 GlobalSection: []*Global{ 912 { 913 Init: &ConstantExpression{ 914 Opcode: OpcodeI32Const, // not funcref. 915 Data: leb128.EncodeInt32(-1), 916 }, 917 }, 918 { 919 Init: &ConstantExpression{ 920 Opcode: OpcodeRefFunc, 921 Data: leb128.EncodeInt32(123), 922 }, 923 }, 924 }, 925 ElementSection: []*ElementSegment{ 926 { 927 Mode: ElementModeActive, 928 Init: []*Index{uint32Ptr(0), nil, uint32Ptr(5)}, 929 }, 930 { 931 Mode: ElementModeDeclarative, 932 Init: []*Index{uint32Ptr(1), nil, uint32Ptr(5)}, 933 }, 934 { 935 Mode: ElementModePassive, 936 Init: []*Index{uint32Ptr(5), uint32Ptr(2), nil, nil}, 937 }, 938 }, 939 }, 940 exp: map[uint32]struct{}{0: {}, 1: {}, 2: {}, 5: {}, 123: {}, 1000: {}}, 941 }, 942 { 943 mod: &Module{ 944 GlobalSection: []*Global{ 945 { 946 Init: &ConstantExpression{ 947 Opcode: OpcodeRefFunc, 948 Data: nil, 949 }, 950 }, 951 }, 952 }, 953 name: "invalid global", 954 expErr: `global[0] failed to initialize: EOF`, 955 }, 956 } 957 958 for _, tt := range tests { 959 tc := tt 960 t.Run(tc.name, func(t *testing.T) { 961 actual, err := tc.mod.declaredFunctionIndexes() 962 if tc.expErr != "" { 963 require.EqualError(t, err, tc.expErr) 964 } else { 965 require.NoError(t, err) 966 require.Equal(t, tc.exp, actual) 967 } 968 }) 969 } 970 }