1 use wasmtime::*; 2 3 fn field(heap_ty: HeapType) -> FieldType { 4 FieldType::new( 5 Mutability::Var, 6 StorageType::ValType(RefType::new(true, heap_ty).into()), 7 ) 8 } 9 10 fn imm_field(heap_ty: HeapType) -> FieldType { 11 FieldType::new( 12 Mutability::Const, 13 StorageType::ValType(RefType::new(true, heap_ty).into()), 14 ) 15 } 16 17 fn valty(heap_ty: HeapType) -> ValType { 18 ValType::Ref(RefType::new(true, heap_ty)) 19 } 20 21 #[test] 22 fn basic_array_types() -> Result<()> { 23 let engine = Engine::default(); 24 for mutability in [Mutability::Const, Mutability::Var] { 25 for storage_ty in [ 26 StorageType::I8, 27 StorageType::I16, 28 StorageType::ValType(ValType::I32), 29 StorageType::ValType(RefType::new(true, FuncType::new(&engine, [], []).into()).into()), 30 ] { 31 let field_ty = FieldType::new(mutability, storage_ty.clone()); 32 assert_eq!(field_ty.mutability(), mutability); 33 assert!(StorageType::eq(field_ty.element_type(), &storage_ty)); 34 35 let array_ty = ArrayType::new(&engine, field_ty.clone()); 36 assert!(Engine::same(array_ty.engine(), &engine)); 37 assert!(FieldType::eq(&array_ty.field_type(), &field_ty)); 38 assert_eq!(array_ty.mutability(), mutability); 39 assert!(StorageType::eq(&array_ty.element_type(), &storage_ty)); 40 } 41 } 42 Ok(()) 43 } 44 45 #[test] 46 fn empty_struct_type() -> Result<()> { 47 let engine = Engine::default(); 48 let struct_ty = StructType::new(&engine, [])?; 49 assert_eq!(struct_ty.fields().len(), 0); 50 assert!(struct_ty.field(0).is_none()); 51 Ok(()) 52 } 53 54 #[test] 55 fn basic_struct_types() -> Result<()> { 56 let engine = Engine::default(); 57 58 let field_types = || { 59 [Mutability::Const, Mutability::Var] 60 .into_iter() 61 .flat_map(|mutability| { 62 [ 63 StorageType::I8, 64 StorageType::I16, 65 StorageType::ValType(ValType::I32), 66 StorageType::ValType( 67 RefType::new(true, FuncType::new(&engine, [], []).into()).into(), 68 ), 69 ] 70 .into_iter() 71 .map(move |storage_ty| FieldType::new(mutability, storage_ty)) 72 }) 73 }; 74 75 let struct_ty = StructType::new(&engine, field_types())?; 76 77 assert_eq!(struct_ty.fields().len(), field_types().count()); 78 for ((i, expected), actual) in field_types().enumerate().zip(struct_ty.fields()) { 79 assert!(FieldType::eq(&expected, &actual)); 80 assert!(FieldType::eq(&expected, &struct_ty.field(i).unwrap())); 81 } 82 assert!(struct_ty.field(struct_ty.fields().len()).is_none()); 83 84 Ok(()) 85 } 86 87 #[test] 88 fn struct_type_matches() -> Result<()> { 89 let engine = Engine::default(); 90 91 let super_ty = StructType::with_finality_and_supertype( 92 &engine, 93 Finality::NonFinal, 94 None, 95 [imm_field(HeapType::Func)], 96 )?; 97 98 // Depth. 99 let sub_ty = StructType::with_finality_and_supertype( 100 &engine, 101 Finality::Final, 102 Some(&super_ty), 103 [imm_field(HeapType::NoFunc)], 104 )?; 105 assert!(sub_ty.matches(&super_ty)); 106 let not_sub_ty = StructType::new(&engine, [imm_field(HeapType::NoFunc)])?; 107 assert!(!not_sub_ty.matches(&super_ty)); 108 109 // Width. 110 let sub_ty = StructType::with_finality_and_supertype( 111 &engine, 112 Finality::Final, 113 Some(&super_ty), 114 [imm_field(HeapType::Func), imm_field(HeapType::Extern)], 115 )?; 116 assert!(sub_ty.matches(&super_ty)); 117 let not_sub_ty = StructType::new( 118 &engine, 119 [imm_field(HeapType::Func), imm_field(HeapType::Extern)], 120 )?; 121 assert!(!not_sub_ty.matches(&super_ty)); 122 123 // Depth and width. 124 let sub_ty = StructType::with_finality_and_supertype( 125 &engine, 126 Finality::Final, 127 Some(&super_ty), 128 [imm_field(HeapType::NoFunc), imm_field(HeapType::Extern)], 129 )?; 130 assert!(sub_ty.matches(&super_ty)); 131 let not_sub_ty = StructType::new( 132 &engine, 133 [imm_field(HeapType::NoFunc), imm_field(HeapType::Extern)], 134 )?; 135 assert!(!not_sub_ty.matches(&super_ty)); 136 137 // Unrelated structs. 138 let not_sub_ty = StructType::new(&engine, [imm_field(HeapType::Extern)])?; 139 assert!(!not_sub_ty.matches(&super_ty)); 140 let not_sub_ty = StructType::new(&engine, [field(HeapType::Extern)])?; 141 assert!(!not_sub_ty.matches(&super_ty)); 142 let not_sub_ty = StructType::new(&engine, [])?; 143 assert!(!not_sub_ty.matches(&super_ty)); 144 145 Ok(()) 146 } 147 148 #[test] 149 fn struct_subtyping_fields_must_match() -> Result<()> { 150 let engine = Engine::default(); 151 152 let a = StructType::with_finality_and_supertype( 153 &engine, 154 Finality::NonFinal, 155 None, 156 [imm_field(HeapType::Any)], 157 )?; 158 159 for (msg, expected, fields) in [ 160 ("Missing field", false, vec![]), 161 ( 162 "Non-matching field", 163 false, 164 vec![imm_field(HeapType::Extern)], 165 ), 166 ("Wrong mutability field", false, vec![field(HeapType::Any)]), 167 ("Exact match is okay", true, vec![imm_field(HeapType::Any)]), 168 ( 169 "Subtype of the field is okay", 170 true, 171 vec![imm_field(HeapType::Eq)], 172 ), 173 ( 174 "Extra fields are okay", 175 true, 176 vec![imm_field(HeapType::Any), imm_field(HeapType::Extern)], 177 ), 178 ] { 179 let actual = 180 StructType::with_finality_and_supertype(&engine, Finality::NonFinal, Some(&a), fields) 181 .is_ok(); 182 assert_eq!( 183 expected, actual, 184 "expected valid? {expected}; actually valid? {actual}; {msg}" 185 ); 186 } 187 188 Ok(()) 189 } 190 191 #[test] 192 fn struct_subtyping_supertype_and_finality() -> Result<()> { 193 let engine = Engine::default(); 194 195 for (expected, finality) in [(true, Finality::NonFinal), (false, Finality::Final)] { 196 let a = StructType::with_finality_and_supertype(&engine, finality, None, [])?; 197 let actual = 198 StructType::with_finality_and_supertype(&engine, Finality::Final, Some(&a), []).is_ok(); 199 assert_eq!(expected, actual); 200 } 201 202 Ok(()) 203 } 204 205 #[test] 206 fn struct_subtyping() -> Result<()> { 207 let engine = Engine::default(); 208 209 // These types produce the following trees: 210 // 211 // base g 212 // / \ / 213 // a b h 214 // / \ / 215 // c d i 216 // / 217 // e 218 // / 219 // f 220 let base = StructType::with_finality_and_supertype(&engine, Finality::NonFinal, None, [])?; 221 let a = StructType::with_finality_and_supertype(&engine, Finality::NonFinal, Some(&base), [])?; 222 let b = StructType::with_finality_and_supertype( 223 &engine, 224 Finality::NonFinal, 225 Some(&base), 226 // Have to add a field so that `b` doesn't dedupe to `a`. 227 [field(HeapType::Any)], 228 )?; 229 let c = StructType::with_finality_and_supertype(&engine, Finality::NonFinal, Some(&a), [])?; 230 let d = StructType::with_finality_and_supertype( 231 &engine, 232 Finality::NonFinal, 233 Some(&a), 234 // Have to add a field so that `d` doesn't dedupe to `c`. 235 [field(HeapType::Any)], 236 )?; 237 let e = StructType::with_finality_and_supertype(&engine, Finality::NonFinal, Some(&c), [])?; 238 let f = StructType::with_finality_and_supertype(&engine, Finality::NonFinal, Some(&e), [])?; 239 let g = StructType::with_finality_and_supertype( 240 &engine, 241 Finality::NonFinal, 242 None, 243 // Have to add a field so that `g` doesn't dedupe to `base`. 244 [field(HeapType::Any)], 245 )?; 246 let h = StructType::with_finality_and_supertype( 247 &engine, 248 Finality::NonFinal, 249 Some(&g), 250 [field(HeapType::Any)], 251 )?; 252 let i = StructType::with_finality_and_supertype( 253 &engine, 254 Finality::NonFinal, 255 Some(&h), 256 [field(HeapType::Any)], 257 )?; 258 259 for (expected, sub_name, sub, sup_name, sup) in [ 260 // Identity, at root. 261 (true, "base", &base, "base", &base), 262 // Identity, in middle. 263 (true, "c", &c, "c", &c), 264 // Identity, at leaf. 265 (true, "f", &f, "f", &f), 266 // Direct, at root. 267 (true, "a", &a, "base", &base), 268 // Direct, in middle. 269 (true, "c", &c, "a", &a), 270 // Direct, at leaf. 271 (true, "f", &f, "e", &e), 272 // Transitive, at root. 273 (true, "c", &c, "base", &base), 274 // Transitive, in middle. 275 (true, "e", &e, "a", &a), 276 // Transitive, at leaf. 277 (true, "f", &f, "c", &c), 278 // Unrelated roots. 279 (false, "base", &base, "g", &g), 280 (false, "g", &g, "base", &base), 281 // Unrelated siblings. 282 (false, "a", &a, "b", &b), 283 (false, "b", &b, "a", &a), 284 (false, "c", &c, "d", &d), 285 (false, "d", &d, "c", &c), 286 // Unrelated root and middle. 287 (false, "base", &base, "h", &h), 288 (false, "h", &h, "base", &base), 289 // Unrelated root and leaf. 290 (false, "base", &base, "i", &i), 291 (false, "i", &i, "base", &base), 292 // Unrelated middles. 293 (false, "a", &a, "h", &h), 294 (false, "h", &h, "a", &a), 295 // Unrelated middle and leaf. 296 (false, "a", &a, "i", &i), 297 (false, "i", &i, "a", &a), 298 ] { 299 eprintln!("expect that `{sub_name} <: {sup_name}` is `{expected}`"); 300 let sub = HeapType::ConcreteStruct(sub.clone()); 301 let sup = HeapType::ConcreteStruct(sup.clone()); 302 let actual = sub.matches(&sup); 303 assert_eq!(expected, actual); 304 } 305 306 Ok(()) 307 } 308 309 #[test] 310 fn array_subtyping_field_must_match() -> Result<()> { 311 let engine = Engine::default(); 312 313 let a = ArrayType::with_finality_and_supertype( 314 &engine, 315 Finality::NonFinal, 316 None, 317 imm_field(HeapType::Any), 318 )?; 319 320 for (expected, field) in [ 321 // Non-matching field. 322 (false, imm_field(HeapType::Extern)), 323 // Wrong mutability field. 324 (false, field(HeapType::Any)), 325 // Exact match is okay. 326 (true, imm_field(HeapType::Any)), 327 // Subtype of the field is okay. 328 (true, imm_field(HeapType::Eq)), 329 ] { 330 let actual = 331 ArrayType::with_finality_and_supertype(&engine, Finality::NonFinal, Some(&a), field) 332 .is_ok(); 333 assert_eq!(expected, actual); 334 } 335 336 Ok(()) 337 } 338 339 #[test] 340 fn array_subtyping_supertype_and_finality() -> Result<()> { 341 let engine = Engine::default(); 342 343 for (expected, finality) in [(true, Finality::NonFinal), (false, Finality::Final)] { 344 let superty = 345 ArrayType::with_finality_and_supertype(&engine, finality, None, field(HeapType::Any))?; 346 let actual = ArrayType::with_finality_and_supertype( 347 &engine, 348 Finality::Final, 349 Some(&superty), 350 field(HeapType::Any), 351 ) 352 .is_ok(); 353 assert_eq!(expected, actual); 354 } 355 356 Ok(()) 357 } 358 359 #[test] 360 fn array_subtyping() -> Result<()> { 361 let engine = Engine::default(); 362 363 // These types produce the following trees: 364 // 365 // base g 366 // / \ / 367 // a b h 368 // / \ / 369 // c d i 370 // / 371 // e 372 // / 373 // f 374 let base = ArrayType::with_finality_and_supertype( 375 &engine, 376 Finality::NonFinal, 377 None, 378 imm_field(HeapType::Any), 379 )?; 380 let a = ArrayType::with_finality_and_supertype( 381 &engine, 382 Finality::NonFinal, 383 Some(&base), 384 imm_field(HeapType::Any), 385 )?; 386 let b = ArrayType::with_finality_and_supertype( 387 &engine, 388 Finality::NonFinal, 389 Some(&base), 390 imm_field(HeapType::Eq), 391 )?; 392 let c = ArrayType::with_finality_and_supertype( 393 &engine, 394 Finality::NonFinal, 395 Some(&a), 396 imm_field(HeapType::Any), 397 )?; 398 let d = ArrayType::with_finality_and_supertype( 399 &engine, 400 Finality::NonFinal, 401 Some(&a), 402 imm_field(HeapType::Eq), 403 )?; 404 let e = ArrayType::with_finality_and_supertype( 405 &engine, 406 Finality::NonFinal, 407 Some(&c), 408 imm_field(HeapType::Any), 409 )?; 410 let f = ArrayType::with_finality_and_supertype( 411 &engine, 412 Finality::NonFinal, 413 Some(&e), 414 imm_field(HeapType::Any), 415 )?; 416 let g = ArrayType::with_finality_and_supertype( 417 &engine, 418 Finality::NonFinal, 419 None, 420 imm_field(HeapType::Eq), 421 )?; 422 let h = ArrayType::with_finality_and_supertype( 423 &engine, 424 Finality::NonFinal, 425 Some(&g), 426 imm_field(HeapType::Eq), 427 )?; 428 let i = ArrayType::with_finality_and_supertype( 429 &engine, 430 Finality::NonFinal, 431 Some(&h), 432 imm_field(HeapType::Eq), 433 )?; 434 435 for (expected, sub_name, sub, sup_name, sup) in [ 436 // Identity, at root. 437 (true, "base", &base, "base", &base), 438 // Identity, in middle. 439 (true, "c", &c, "c", &c), 440 // Identity, at leaf. 441 (true, "f", &f, "f", &f), 442 // Direct, at root. 443 (true, "a", &a, "base", &base), 444 // Direct, in middle. 445 (true, "c", &c, "a", &a), 446 // Direct, at leaf. 447 (true, "f", &f, "e", &e), 448 // Transitive, at root. 449 (true, "c", &c, "base", &base), 450 // Transitive, in middle. 451 (true, "e", &e, "a", &a), 452 // Transitive, at leaf. 453 (true, "f", &f, "c", &c), 454 // Unrelated roots. 455 (false, "base", &base, "g", &g), 456 (false, "g", &g, "base", &base), 457 // Unrelated siblings. 458 (false, "a", &a, "b", &b), 459 (false, "b", &b, "a", &a), 460 (false, "c", &c, "d", &d), 461 (false, "d", &d, "c", &c), 462 // Unrelated root and middle. 463 (false, "base", &base, "h", &h), 464 (false, "h", &h, "base", &base), 465 // Unrelated root and leaf. 466 (false, "base", &base, "i", &i), 467 (false, "i", &i, "base", &base), 468 // Unrelated middles. 469 (false, "a", &a, "h", &h), 470 (false, "h", &h, "a", &a), 471 // Unrelated middle and leaf. 472 (false, "a", &a, "i", &i), 473 (false, "i", &i, "a", &a), 474 ] { 475 eprintln!("expect that `{sub_name} <: {sup_name}` is `{expected}`"); 476 let sub = HeapType::ConcreteArray(sub.clone()); 477 let sup = HeapType::ConcreteArray(sup.clone()); 478 let actual = sub.matches(&sup); 479 assert_eq!(expected, actual); 480 } 481 482 Ok(()) 483 } 484 485 #[test] 486 fn func_subtyping_field_must_match() -> Result<()> { 487 let engine = Engine::default(); 488 489 let superty = FuncType::with_finality_and_supertype( 490 &engine, 491 Finality::NonFinal, 492 None, 493 [valty(HeapType::Struct)], 494 [valty(HeapType::Any)], 495 )?; 496 497 for (expected, param, ret) in [ 498 // Non-matching param type. 499 (false, valty(HeapType::Extern), valty(HeapType::Any)), 500 // Non-matching return type. 501 (false, valty(HeapType::Struct), valty(HeapType::Extern)), 502 // Exact match is okay. 503 (true, valty(HeapType::Struct), valty(HeapType::Any)), 504 // Subtype of the return type is okay. 505 (true, valty(HeapType::Struct), valty(HeapType::Eq)), 506 // Supertype of the param type is okay. 507 (true, valty(HeapType::Eq), valty(HeapType::Any)), 508 ] { 509 let actual = FuncType::with_finality_and_supertype( 510 &engine, 511 Finality::NonFinal, 512 Some(&superty), 513 [param], 514 [ret], 515 ) 516 .is_ok(); 517 assert_eq!(expected, actual); 518 } 519 520 Ok(()) 521 } 522 523 #[test] 524 fn func_subtyping_supertype_and_finality() -> Result<()> { 525 let engine = Engine::default(); 526 527 for (expected, finality) in [(true, Finality::NonFinal), (false, Finality::Final)] { 528 let superty = FuncType::with_finality_and_supertype( 529 &engine, 530 finality, 531 None, 532 [], 533 [valty(HeapType::Any)], 534 )?; 535 let actual = FuncType::with_finality_and_supertype( 536 &engine, 537 Finality::Final, 538 Some(&superty), 539 [], 540 [valty(HeapType::Any)], 541 ) 542 .is_ok(); 543 assert_eq!(expected, actual); 544 } 545 546 Ok(()) 547 } 548 549 #[test] 550 fn func_subtyping() -> Result<()> { 551 let engine = Engine::default(); 552 553 // These types produce the following trees: 554 // 555 // base g 556 // / \ / 557 // a b h 558 // / \ / 559 // c d i 560 // / 561 // e 562 // / 563 // f 564 let base = FuncType::with_finality_and_supertype( 565 &engine, 566 Finality::NonFinal, 567 None, 568 [], 569 [valty(HeapType::Any)], 570 )?; 571 let a = FuncType::with_finality_and_supertype( 572 &engine, 573 Finality::NonFinal, 574 Some(&base), 575 [], 576 [valty(HeapType::Any)], 577 )?; 578 let b = FuncType::with_finality_and_supertype( 579 &engine, 580 Finality::NonFinal, 581 Some(&base), 582 [], 583 [valty(HeapType::Eq)], 584 )?; 585 let c = FuncType::with_finality_and_supertype( 586 &engine, 587 Finality::NonFinal, 588 Some(&a), 589 [], 590 [valty(HeapType::Any)], 591 )?; 592 let d = FuncType::with_finality_and_supertype( 593 &engine, 594 Finality::NonFinal, 595 Some(&a), 596 [], 597 [valty(HeapType::Eq)], 598 )?; 599 let e = FuncType::with_finality_and_supertype( 600 &engine, 601 Finality::NonFinal, 602 Some(&c), 603 [], 604 [valty(HeapType::Any)], 605 )?; 606 let f = FuncType::with_finality_and_supertype( 607 &engine, 608 Finality::NonFinal, 609 Some(&e), 610 [], 611 [valty(HeapType::Any)], 612 )?; 613 let g = FuncType::with_finality_and_supertype( 614 &engine, 615 Finality::NonFinal, 616 None, 617 [], 618 [valty(HeapType::Eq)], 619 )?; 620 let h = FuncType::with_finality_and_supertype( 621 &engine, 622 Finality::NonFinal, 623 Some(&g), 624 [], 625 [valty(HeapType::Eq)], 626 )?; 627 let i = FuncType::with_finality_and_supertype( 628 &engine, 629 Finality::NonFinal, 630 Some(&h), 631 [], 632 [valty(HeapType::Eq)], 633 )?; 634 635 for (expected, sub_name, sub, sup_name, sup) in [ 636 // Identity, at root. 637 (true, "base", &base, "base", &base), 638 // Identity, in middle. 639 (true, "c", &c, "c", &c), 640 // Identity, at leaf. 641 (true, "f", &f, "f", &f), 642 // Direct, at root. 643 (true, "a", &a, "base", &base), 644 // Direct, in middle. 645 (true, "c", &c, "a", &a), 646 // Direct, at leaf. 647 (true, "f", &f, "e", &e), 648 // Transitive, at root. 649 (true, "c", &c, "base", &base), 650 // Transitive, in middle. 651 (true, "e", &e, "a", &a), 652 // Transitive, at leaf. 653 (true, "f", &f, "c", &c), 654 // Unrelated roots. 655 (false, "base", &base, "g", &g), 656 (false, "g", &g, "base", &base), 657 // Unrelated siblings. 658 (false, "a", &a, "b", &b), 659 (false, "b", &b, "a", &a), 660 (false, "c", &c, "d", &d), 661 (false, "d", &d, "c", &c), 662 // Unrelated root and middle. 663 (false, "base", &base, "h", &h), 664 (false, "h", &h, "base", &base), 665 // Unrelated root and leaf. 666 (false, "base", &base, "i", &i), 667 (false, "i", &i, "base", &base), 668 // Unrelated middles. 669 (false, "a", &a, "h", &h), 670 (false, "h", &h, "a", &a), 671 // Unrelated middle and leaf. 672 (false, "a", &a, "i", &i), 673 (false, "i", &i, "a", &a), 674 ] { 675 eprintln!("expect that `{sub_name} <: {sup_name}` is `{expected}`"); 676 let sub = HeapType::ConcreteFunc(sub.clone()); 677 let sup = HeapType::ConcreteFunc(sup.clone()); 678 let actual = sub.matches(&sup); 679 assert_eq!(expected, actual); 680 } 681 682 Ok(()) 683 } 684 685 #[test] 686 fn heap_type_matches_noexn() -> Result<()> { 687 // Test that NoExn only matches exception-related types. 688 // NoExn should NOT match unrelated heap types like Func, Extern, Any, etc. 689 690 // NoExn should match exception hierarchy types 691 assert!(HeapType::NoExn.matches(&HeapType::Exn)); 692 assert!(HeapType::NoExn.matches(&HeapType::NoExn)); 693 694 // NoExn should NOT match types outside the exception hierarchy 695 assert!(!HeapType::NoExn.matches(&HeapType::Func)); 696 assert!(!HeapType::NoExn.matches(&HeapType::NoFunc)); 697 assert!(!HeapType::NoExn.matches(&HeapType::Extern)); 698 assert!(!HeapType::NoExn.matches(&HeapType::NoExtern)); 699 assert!(!HeapType::NoExn.matches(&HeapType::Any)); 700 assert!(!HeapType::NoExn.matches(&HeapType::Eq)); 701 assert!(!HeapType::NoExn.matches(&HeapType::I31)); 702 assert!(!HeapType::NoExn.matches(&HeapType::Struct)); 703 assert!(!HeapType::NoExn.matches(&HeapType::Array)); 704 assert!(!HeapType::NoExn.matches(&HeapType::None)); 705 assert!(!HeapType::NoExn.matches(&HeapType::Cont)); 706 assert!(!HeapType::NoExn.matches(&HeapType::NoCont)); 707 708 Ok(()) 709 } 710