1 #include "utils.h" 2 3 #include <gtest/gtest.h> 4 #include <wasmtime.h> 5 #include <wasmtime/component.hh> 6 #include <wasmtime/store.hh> 7 8 #include <array> 9 #include <format> 10 #include <optional> 11 #include <span> 12 #include <variant> 13 14 using namespace wasmtime::component; 15 using wasmtime::Engine; 16 using wasmtime::Result; 17 using wasmtime::Span; 18 using wasmtime::Store; 19 20 static std::string echo_component(std::string_view type, std::string_view func, 21 std::string_view host_params) { 22 return std::format( 23 R"END( 24 (component 25 (type $Foo' {}) 26 (import "foo" (type $Foo (eq $Foo'))) 27 (import "do" (func $do (param "a" $Foo) (result $Foo))) 28 (core module $libc 29 (memory (export "memory") 1) 30 {} 31 ) 32 (core instance $libc (instantiate $libc)) 33 (core func $do_lower (canon lower (func $do) (memory $libc "memory") (realloc (func $libc "realloc")))) 34 35 (core module $doer 36 (import "host" "do" (func $do {})) 37 (import "libc" "memory" (memory 1)) 38 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) 39 40 (func (export "call") 41 {}) 42 ) 43 (core instance $doer (instantiate $doer 44 (with "host" (instance (export "do" (func $do_lower)))) 45 (with "libc" (instance $libc)) 46 )) 47 48 (func $call 49 (param "a" $Foo) 50 (result $Foo) 51 (canon lift 52 (core func $doer "call") 53 (memory $libc "memory") 54 (realloc (func $libc "realloc"))) 55 ) 56 57 (export "call" (func $call)) 58 ) 59 )END", 60 type, REALLOC_AND_FREE, host_params, func); 61 } 62 63 struct Context { 64 Store store; 65 Store::Context context; 66 Instance instance; 67 Func func; 68 69 static Context New(Engine &engine, std::string_view component_text, 70 Linker &linker) { 71 Store store(engine); 72 const auto context = store.context(); 73 Component component = Component::compile(engine, component_text).unwrap(); 74 75 auto f = component.export_index(nullptr, "call"); 76 77 EXPECT_TRUE(f); 78 79 auto instance = linker.instantiate(context, component).unwrap(); 80 auto func = *instance.get_func(context, *f); 81 82 return Context{ 83 .store = std::move(store), 84 .context = context, 85 .instance = instance, 86 .func = func, 87 }; 88 } 89 }; 90 91 typedef Result<std::monostate> (*host_func_t)(Store::Context, const FuncType &, 92 Span<const Val>, Span<Val>); 93 94 static Context create(std::string_view type, std::string_view body, 95 std::string_view host_params, host_func_t callback) { 96 Engine engine; 97 Linker linker(engine); 98 linker.root().add_func("do", callback).unwrap(); 99 auto component_text = echo_component(type, body, host_params); 100 return Context::New(engine, component_text, linker); 101 } 102 103 TEST(component, value_record) { 104 static const auto check = [](const Val &v, uint64_t x, uint64_t y) { 105 EXPECT_TRUE(v.is_record()); 106 const Record &r = v.get_record(); 107 EXPECT_EQ(r.size(), 2); 108 109 const auto &x_field = *r.begin(); 110 EXPECT_EQ(x_field.name(), "x"); 111 const auto &x_field_val = x_field.value(); 112 EXPECT_TRUE(x_field_val.is_u64()); 113 EXPECT_EQ(x_field_val.get_u64(), x); 114 115 const auto &y_field = *(r.begin() + 1); 116 EXPECT_EQ(y_field.name(), "y"); 117 const auto &y_field_val = y_field.value(); 118 EXPECT_TRUE(y_field_val.is_u64()); 119 EXPECT_EQ(y_field_val.get_u64(), y); 120 }; 121 122 static const auto make = [](uint64_t x, uint64_t y) -> Val { 123 return Record({ 124 {"x", x}, 125 {"y", y}, 126 }); 127 }; 128 129 auto ctx = create( 130 R"((record (field "x" u64) (field "y" u64)))", R"( 131 (param $x i64) 132 (param $y i64) 133 (result i32) 134 (local $res i32) 135 local.get $x 136 local.get $y 137 (call $realloc 138 (i32.const 0) 139 (i32.const 0) 140 (i32.const 4) 141 (i32.const 16)) 142 local.tee $res 143 call $do 144 local.get $res 145 )", 146 "(param i64 i64 i32)", 147 +[](Store::Context, const FuncType &_ty, Span<const Val> args, 148 Span<Val> rets) -> Result<std::monostate> { 149 EXPECT_EQ(args.size(), 1); 150 check(args[0], 1, 2); 151 152 EXPECT_EQ(rets.size(), 1); 153 rets[0] = make(3, 4); 154 155 return std::monostate(); 156 }); 157 158 auto arg = make(1, 2); 159 auto res = Val(false); 160 161 ctx.func.call(ctx.context, Span<const Val>(&arg, 1), Span<Val>(&res, 1)) 162 .unwrap(); 163 ctx.func.post_return(ctx.context).unwrap(); 164 165 check(res, 3, 4); 166 } 167 168 TEST(component, value_string) { 169 static const auto check = [](const Val &v, std::string_view text) { 170 EXPECT_TRUE(v.is_string()); 171 EXPECT_EQ(v.get_string(), text); 172 }; 173 174 static const auto make = [](std::string_view text) -> Val { 175 return Val::string(text); 176 }; 177 178 auto ctx = create( 179 R"(string)", R"( 180 (param $x i32) 181 (param $y i32) 182 (result i32) 183 (local $res i32) 184 local.get $x 185 local.get $y 186 (call $realloc 187 (i32.const 0) 188 (i32.const 0) 189 (i32.const 4) 190 (i32.const 8)) 191 local.tee $res 192 call $do 193 local.get $res 194 )", 195 "(param i32 i32 i32)", 196 +[](Store::Context, const FuncType &, Span<const Val> args, 197 Span<Val> rets) -> Result<std::monostate> { 198 EXPECT_EQ(args.size(), 1); 199 check(args[0], "hello from A!"); 200 201 EXPECT_EQ(rets.size(), 1); 202 rets[0] = make("hello from B!"); 203 204 return std::monostate(); 205 }); 206 207 auto arg = make("hello from A!"); 208 auto res = Val(false); 209 210 ctx.func.call(ctx.context, Span<const Val>(&arg, 1), Span<Val>(&res, 1)) 211 .unwrap(); 212 ctx.func.post_return(ctx.context).unwrap(); 213 214 check(res, "hello from B!"); 215 } 216 217 TEST(component, value_list) { 218 static const auto check = [](const Val &v, std::vector<uint32_t> data) { 219 EXPECT_TRUE(v.is_list()); 220 const List &l = v.get_list(); 221 EXPECT_EQ(l.size(), data.size()); 222 223 for (auto i = 0; i < data.size(); i++) { 224 const auto &elem = l.begin()[i]; 225 EXPECT_TRUE(elem.is_u32()); 226 EXPECT_EQ(elem.get_u32(), data[i]); 227 } 228 }; 229 230 static const auto make = [](std::vector<Val> data) -> Val { 231 return List(data); 232 }; 233 234 auto ctx = create( 235 R"((list u32))", R"( 236 (param $x i32) 237 (param $y i32) 238 (result i32) 239 (local $res i32) 240 local.get $x 241 local.get $y 242 (call $realloc 243 (i32.const 0) 244 (i32.const 0) 245 (i32.const 4) 246 (i32.const 8)) 247 local.tee $res 248 call $do 249 local.get $res 250 )", 251 "(param i32 i32 i32)", 252 +[](Store::Context, const FuncType &, Span<const Val> args, 253 Span<Val> rets) -> Result<std::monostate> { 254 EXPECT_EQ(args.size(), 1); 255 check(args[0], {1, 2, 3}); 256 257 EXPECT_EQ(rets.size(), 1); 258 rets[0] = make({uint32_t(4), uint32_t(5), uint32_t(6), uint32_t(7)}); 259 260 return std::monostate(); 261 }); 262 263 auto arg = make({uint32_t(1), uint32_t(2), uint32_t(3)}); 264 auto res = Val(false); 265 266 ctx.func.call(ctx.context, Span<const Val>(&arg, 1), Span<Val>(&res, 1)) 267 .unwrap(); 268 ctx.func.post_return(ctx.context).unwrap(); 269 270 check(res, {4, 5, 6, 7}); 271 } 272 273 TEST(component, value_tuple) { 274 static const auto check = [](const Val &v, std::vector<uint32_t> data) { 275 EXPECT_TRUE(v.is_tuple()); 276 const Tuple &t = v.get_tuple(); 277 EXPECT_EQ(t.size(), data.size()); 278 for (auto i = 0; i < data.size(); i++) { 279 const auto &elem = t.begin()[i]; 280 EXPECT_TRUE(elem.is_u32()); 281 EXPECT_EQ(elem.get_u32(), data[i]); 282 } 283 }; 284 285 static const auto make = [](std::vector<Val> data) -> Val { 286 return Tuple(data); 287 }; 288 289 auto ctx = create( 290 R"((tuple u32 u32 u32))", R"( 291 (param $x i32) 292 (param $y i32) 293 (param $z i32) 294 (result i32) 295 (local $res i32) 296 local.get $x 297 local.get $y 298 local.get $z 299 (call $realloc 300 (i32.const 0) 301 (i32.const 0) 302 (i32.const 4) 303 (i32.const 12)) 304 local.tee $res 305 call $do 306 local.get $res 307 )", 308 "(param i32 i32 i32 i32)", 309 +[](Store::Context, const FuncType &, Span<const Val> args, 310 Span<Val> rets) -> Result<std::monostate> { 311 EXPECT_EQ(args.size(), 1); 312 check(args[0], {1, 2, 3}); 313 314 EXPECT_EQ(rets.size(), 1); 315 rets[0] = make({uint32_t(4), uint32_t(5), uint32_t(6)}); 316 317 return std::monostate(); 318 }); 319 320 auto arg = make({uint32_t(1), uint32_t(2), uint32_t(3)}); 321 auto res = Val(false); 322 323 ctx.func.call(ctx.context, Span<const Val>(&arg, 1), Span<Val>(&res, 1)) 324 .unwrap(); 325 ctx.func.post_return(ctx.context).unwrap(); 326 327 check(res, {4, 5, 6}); 328 } 329 330 TEST(component, value_variant) { 331 static const auto check_aa = [](const Val &v, uint32_t value) { 332 EXPECT_TRUE(v.is_variant()); 333 const Variant &var = v.get_variant(); 334 EXPECT_EQ(var.discriminant(), "aa"); 335 EXPECT_NE(var.value(), nullptr); 336 EXPECT_TRUE(var.value()->is_u32()); 337 EXPECT_EQ(var.value()->get_u32(), value); 338 }; 339 340 static const auto check_bb = [](const Val &v, std::string_view value) { 341 EXPECT_TRUE(v.is_variant()); 342 const Variant &var = v.get_variant(); 343 EXPECT_EQ(var.discriminant(), "bb"); 344 EXPECT_NE(var.value(), nullptr); 345 EXPECT_TRUE(var.value()->is_string()); 346 EXPECT_EQ(var.value()->get_string(), value); 347 }; 348 349 static const auto make_aa = [](uint32_t value) -> Val { 350 return Variant("aa", Val(value)); 351 }; 352 353 static const auto make_bb = [](std::string_view value) -> Val { 354 return Variant("bb", Val::string(value)); 355 }; 356 357 auto ctx = create( 358 R"( 359 (variant 360 (case "aa" u32) 361 (case "bb" string) 362 ) 363 )", 364 R"( 365 (param $x i32) 366 (param $y i32) 367 (param $z i32) 368 (result i32) 369 (local $res i32) 370 local.get $x 371 local.get $y 372 local.get $z 373 (call $realloc 374 (i32.const 0) 375 (i32.const 0) 376 (i32.const 4) 377 (i32.const 12)) 378 local.tee $res 379 call $do 380 local.get $res 381 )", 382 "(param i32 i32 i32 i32)", 383 +[](Store::Context, const FuncType &, Span<const Val> args, 384 Span<Val> rets) -> Result<std::monostate> { 385 EXPECT_EQ(args.size(), 1); 386 check_aa(args[0], 123); 387 388 EXPECT_EQ(rets.size(), 1); 389 rets[0] = make_bb("textt"); 390 391 return std::monostate(); 392 }); 393 394 auto arg = make_aa(123); 395 auto res = Val(false); 396 397 ctx.func.call(ctx.context, Span<const Val>(&arg, 1), Span<Val>(&res, 1)) 398 .unwrap(); 399 ctx.func.post_return(ctx.context).unwrap(); 400 401 check_bb(res, "textt"); 402 } 403 404 TEST(component, value_enum) { 405 static const auto check = [](const Val &v, std::string_view text) { 406 EXPECT_TRUE(v.is_enum()); 407 EXPECT_EQ(v.get_enum(), text); 408 }; 409 410 static const auto make = [](std::string_view text) -> Val { 411 return Val::enum_(text); 412 }; 413 414 auto ctx = create( 415 R"((enum "aa" "bb"))", R"( 416 (param $x i32) 417 (result i32) 418 local.get $x 419 call $do 420 )", 421 "(param i32) (result i32)", 422 +[](Store::Context, const FuncType &, Span<const Val> args, 423 Span<Val> rets) -> Result<std::monostate> { 424 EXPECT_EQ(args.size(), 1); 425 check(args[0], "aa"); 426 427 EXPECT_EQ(rets.size(), 1); 428 rets[0] = make("bb"); 429 430 return std::monostate(); 431 }); 432 433 auto arg = make("aa"); 434 auto res = Val(false); 435 436 ctx.func.call(ctx.context, Span<const Val>(&arg, 1), Span<Val>(&res, 1)) 437 .unwrap(); 438 ctx.func.post_return(ctx.context).unwrap(); 439 440 check(res, "bb"); 441 } 442 443 TEST(component, value_option) { 444 static const auto check = [](const Val &v, std::optional<uint32_t> value) { 445 EXPECT_TRUE(v.is_option()); 446 const WitOption &o = v.get_option(); 447 if (value.has_value()) { 448 EXPECT_NE(o.value(), nullptr); 449 EXPECT_TRUE(o.value()->is_u32()); 450 EXPECT_EQ(o.value()->get_u32(), *value); 451 } else { 452 EXPECT_EQ(o.value(), nullptr); 453 } 454 }; 455 456 static const auto make = [](std::optional<uint32_t> value) -> Val { 457 if (value) { 458 return WitOption(Val(*value)); 459 } 460 return WitOption(std::nullopt); 461 }; 462 463 auto ctx = create( 464 R"((option u32))", R"( 465 (param $x i32) 466 (param $y i32) 467 (result i32) 468 (local $res i32) 469 local.get $x 470 local.get $y 471 (call $realloc 472 (i32.const 0) 473 (i32.const 0) 474 (i32.const 4) 475 (i32.const 8)) 476 local.tee $res 477 call $do 478 local.get $res 479 )", 480 "(param i32 i32 i32)", 481 +[](Store::Context, const FuncType &, Span<const Val> args, 482 Span<Val> rets) -> Result<std::monostate> { 483 EXPECT_EQ(args.size(), 1); 484 check(args[0], 123); 485 486 EXPECT_EQ(rets.size(), 1); 487 rets[0] = make({}); 488 489 return std::monostate(); 490 }); 491 492 auto arg = make(123); 493 auto res = Val(false); 494 495 ctx.func.call(ctx.context, Span<const Val>(&arg, 1), Span<Val>(&res, 1)) 496 .unwrap(); 497 ctx.func.post_return(ctx.context).unwrap(); 498 499 check(res, {}); 500 } 501 502 TEST(component, value_result) { 503 static const auto check = [](const Val &v, bool expected_is_ok, 504 uint32_t expected_value) { 505 EXPECT_TRUE(v.is_result()); 506 const WitResult &r = v.get_result(); 507 EXPECT_EQ(r.is_ok(), expected_is_ok); 508 EXPECT_NE(r.payload(), nullptr); 509 EXPECT_TRUE(r.payload()->is_u32()); 510 EXPECT_EQ(r.payload()->get_u32(), expected_value); 511 }; 512 513 static const auto make = [](bool is_ok, uint32_t value) -> Val { 514 if (is_ok) { 515 return WitResult::ok(Val(value)); 516 } 517 return WitResult::err(Val(value)); 518 }; 519 520 auto ctx = create( 521 R"((result u32 (error u32)))", R"( 522 (param $x i32) 523 (param $y i32) 524 (result i32) 525 (local $res i32) 526 local.get $x 527 local.get $y 528 (call $realloc 529 (i32.const 0) 530 (i32.const 0) 531 (i32.const 4) 532 (i32.const 8)) 533 local.tee $res 534 call $do 535 local.get $res 536 )", 537 "(param i32 i32 i32)", 538 +[](Store::Context, const FuncType &, Span<const Val> args, 539 Span<Val> rets) -> Result<std::monostate> { 540 EXPECT_EQ(args.size(), 1); 541 check(args[0], true, 123); 542 543 EXPECT_EQ(rets.size(), 1); 544 rets[0] = make(false, 456); 545 546 return std::monostate(); 547 }); 548 549 auto arg = make(true, 123); 550 auto res = Val(false); 551 552 ctx.func.call(ctx.context, Span<const Val>(&arg, 1), Span<Val>(&res, 1)) 553 .unwrap(); 554 ctx.func.post_return(ctx.context).unwrap(); 555 556 check(res, false, 456); 557 } 558 559 TEST(component, value_flags) { 560 static const auto check = [](const Val &v, std::vector<std::string> data) { 561 EXPECT_TRUE(v.is_flags()); 562 const Flags &f = v.get_flags(); 563 564 EXPECT_EQ(f.size(), data.size()); 565 for (auto i = 0; i < data.size(); i++) { 566 EXPECT_EQ(f.begin()[i].name(), data[i]); 567 } 568 }; 569 570 static const auto make = [](std::vector<Flag> data) -> Val { 571 return Flags(data); 572 }; 573 574 auto ctx = create( 575 R"((flags "aa" "bb"))", R"( 576 (param $x i32) 577 (result i32) 578 local.get $x 579 call $do 580 )", 581 "(param i32) (result i32)", 582 +[](Store::Context, const FuncType &, Span<const Val> args, 583 Span<Val> rets) -> Result<std::monostate> { 584 EXPECT_EQ(args.size(), 1); 585 check(args[0], {"aa"}); 586 587 EXPECT_EQ(rets.size(), 1); 588 rets[0] = make({Flag("aa"), Flag("bb")}); 589 590 return std::monostate(); 591 }); 592 593 auto arg = make({Flag("aa")}); 594 auto res = Val(false); 595 596 ctx.func.call(ctx.context, Span<const Val>(&arg, 1), Span<Val>(&res, 1)) 597 .unwrap(); 598 ctx.func.post_return(ctx.context).unwrap(); 599 600 check(res, {"aa", "bb"}); 601 } 602 603 TEST(component, value_list_inner) { 604 auto x = wasmtime_component_val_t{ 605 .kind = WASMTIME_COMPONENT_LIST, 606 }; 607 wasmtime_component_vallist_new_empty(&x.of.list); 608 EXPECT_EQ(x.of.list.data, nullptr); 609 EXPECT_EQ(x.of.list.size, 0); 610 611 wasmtime_component_vallist_new_uninit(&x.of.list, 1); 612 EXPECT_NE(x.of.list.data, nullptr); 613 EXPECT_EQ(x.of.list.size, 1); 614 615 wasmtime_component_vallist_delete(&x.of.list); 616 617 auto items = std::array{ 618 wasmtime_component_val_t{ 619 .kind = WASMTIME_COMPONENT_U32, 620 .of = {.u32 = 123}, 621 }, 622 }; 623 624 wasmtime_component_vallist_new(&x.of.list, items.size(), items.data()); 625 EXPECT_NE(x.of.list.data, nullptr); 626 EXPECT_EQ(x.of.list.size, 1); 627 628 EXPECT_EQ(x.of.list.data[0].kind, WASMTIME_COMPONENT_U32); 629 EXPECT_EQ(x.of.list.data[0].of.u32, 123); 630 631 auto clone = wasmtime_component_val_t{ 632 .kind = WASMTIME_COMPONENT_LIST, 633 }; 634 635 wasmtime_component_vallist_copy(&clone.of.list, &x.of.list); 636 wasmtime_component_vallist_delete(&x.of.list); 637 638 EXPECT_NE(clone.of.list.data, nullptr); 639 EXPECT_EQ(clone.of.list.size, 1); 640 641 EXPECT_EQ(clone.of.list.data[0].kind, WASMTIME_COMPONENT_U32); 642 EXPECT_EQ(clone.of.list.data[0].of.u32, 123); 643 644 wasmtime_component_vallist_delete(&clone.of.list); 645 } 646 647 TEST(component, records) { 648 Record r({{"x", uint64_t(1)}, {"y", uint64_t(2)}}); 649 EXPECT_EQ(r.size(), 2); 650 651 for (auto &field : r) { 652 if (field.name() == "x") { 653 EXPECT_EQ(field.value().get_u64(), 1); 654 } else if (field.name() == "y") { 655 EXPECT_EQ(field.value().get_u64(), 2); 656 } else { 657 FAIL() << "unexpected field name: " << field.name(); 658 } 659 } 660 661 Record r2({{"x", r}, {"y", uint64_t(2)}}); 662 EXPECT_EQ(r2.size(), 2); 663 EXPECT_EQ(r.size(), 2); 664 665 for (auto &field : r2) { 666 if (field.name() == "x") { 667 auto inner = field.value().get_record(); 668 EXPECT_EQ(inner.size(), 2); 669 for (auto &inner_field : inner) { 670 if (inner_field.name() == "x") { 671 EXPECT_EQ(inner_field.value().get_u64(), 1); 672 } else if (inner_field.name() == "y") { 673 EXPECT_EQ(inner_field.value().get_u64(), 2); 674 } else { 675 FAIL() << "unexpected inner field name: " << inner_field.name(); 676 } 677 } 678 } else if (field.name() == "y") { 679 EXPECT_EQ(field.value().get_u64(), 2); 680 } else { 681 FAIL() << "unexpected field name: " << field.name(); 682 } 683 } 684 685 Val record = r2; 686 EXPECT_TRUE(record.is_record()); 687 EXPECT_EQ(r2.size(), 2); 688 Val record2 = std::move(r2); 689 EXPECT_TRUE(record2.is_record()); 690 EXPECT_EQ(r2.size(), 0); 691 } 692 693 TEST(component, lists) { 694 List l({uint32_t(1), uint32_t(2), uint32_t(3)}); 695 EXPECT_EQ(l.size(), 3); 696 uint32_t expected = 1; 697 for (auto &val : l) { 698 EXPECT_EQ(val.get_u32(), expected); 699 expected++; 700 } 701 702 List l2 = l; 703 EXPECT_EQ(l.size(), 3); 704 EXPECT_EQ(l2.size(), 3); 705 706 List l3 = std::move(l); 707 EXPECT_EQ(l.size(), 0); 708 EXPECT_EQ(l3.size(), 3); 709 710 Val value(l3); 711 value.get_list(); 712 } 713 714 TEST(component, tuples) { 715 Tuple l({uint32_t(1), uint64_t(2), uint8_t(3)}); 716 EXPECT_EQ(l.size(), 3); 717 718 Val value(l); 719 EXPECT_TRUE(value.is_tuple()); 720 EXPECT_EQ(l.size(), 3); 721 722 for (auto &val : l) { 723 if (val.is_u32()) { 724 EXPECT_EQ(val.get_u32(), 1); 725 } else if (val.is_u64()) { 726 EXPECT_EQ(val.get_u64(), 2); 727 } else if (val.is_u8()) { 728 EXPECT_EQ(val.get_u8(), 3); 729 } else { 730 FAIL() << "unexpected tuple value type"; 731 } 732 } 733 } 734 735 TEST(component, variants) { 736 Variant v("hello", uint32_t(42)); 737 EXPECT_EQ(v.discriminant(), "hello"); 738 EXPECT_TRUE(v.value()->is_u32()); 739 EXPECT_EQ(v.value()->get_u32(), 42); 740 741 Variant v2("another", v); 742 EXPECT_EQ(v.discriminant(), "hello"); 743 EXPECT_TRUE(v.value()->is_u32()); 744 EXPECT_EQ(v.value()->get_u32(), 42); 745 EXPECT_EQ(v2.discriminant(), "another"); 746 EXPECT_TRUE(v2.value()->is_variant()); 747 auto inner = v2.value()->get_variant(); 748 EXPECT_EQ(inner.discriminant(), "hello"); 749 EXPECT_TRUE(inner.value()->is_u32()); 750 EXPECT_EQ(inner.value()->get_u32(), 42); 751 752 Val value = v; 753 EXPECT_TRUE(value.is_variant()); 754 auto v3 = value.get_variant(); 755 EXPECT_EQ(v3.discriminant(), "hello"); 756 EXPECT_TRUE(v3.value()->is_u32()); 757 EXPECT_EQ(v3.value()->get_u32(), 42); 758 } 759 760 TEST(component, strings) { 761 Val v = Val::string("hi"); 762 EXPECT_TRUE(v.is_string()); 763 EXPECT_EQ(v.get_string(), "hi"); 764 765 v = Val::string("another"); 766 EXPECT_TRUE(v.is_string()); 767 EXPECT_EQ(v.get_string(), "another"); 768 } 769 770 TEST(component, results) { 771 WitResult r = WitResult::ok(uint32_t(42)); 772 EXPECT_TRUE(r.is_ok()); 773 EXPECT_EQ(r.payload()->get_u32(), 42); 774 775 r = WitResult::ok(std::nullopt); 776 EXPECT_TRUE(r.is_ok()); 777 EXPECT_EQ(r.payload(), nullptr); 778 779 r = WitResult::err(std::nullopt); 780 EXPECT_FALSE(r.is_ok()); 781 EXPECT_EQ(r.payload(), nullptr); 782 783 Val v = r; 784 EXPECT_TRUE(v.is_result()); 785 auto r2 = v.get_result(); 786 EXPECT_FALSE(r2.is_ok()); 787 EXPECT_EQ(r2.payload(), nullptr); 788 789 r = WitResult::ok(uint32_t(99)); 790 v = r; 791 EXPECT_TRUE(r.is_ok()); 792 EXPECT_NE(r.payload(), nullptr); 793 EXPECT_EQ(r.payload()->get_u32(), 99); 794 } 795 796 TEST(component, enums) { 797 Val v = Val::enum_("hi"); 798 EXPECT_TRUE(v.is_enum()); 799 EXPECT_EQ(v.get_enum(), "hi"); 800 801 v = Val::enum_("another"); 802 EXPECT_TRUE(v.is_enum()); 803 EXPECT_EQ(v.get_enum(), "another"); 804 } 805 806 TEST(component, options) { 807 WitOption o(Val(uint32_t(42))); 808 EXPECT_NE(o.value(), nullptr); 809 EXPECT_TRUE(o.value()->is_u32()); 810 EXPECT_EQ(o.value()->get_u32(), 42); 811 812 Val v(o); 813 WitOption o2(v); 814 EXPECT_NE(o.value(), nullptr); 815 EXPECT_TRUE(o2.value()->is_option()); 816 auto inner = o2.value()->get_option(); 817 auto value = inner.value(); 818 EXPECT_NE(value, nullptr); 819 EXPECT_TRUE(value->is_u32()); 820 EXPECT_EQ(value->get_u32(), 42); 821 822 EXPECT_NE(o.value(), nullptr); 823 EXPECT_TRUE(o.value()->is_u32()); 824 EXPECT_EQ(o.value()->get_u32(), 42); 825 826 WitOption o3(std::nullopt); 827 EXPECT_EQ(o3.value(), nullptr); 828 } 829 830 TEST(component, flags) { 831 std::vector<Flag> flags = { 832 Flag("a"), 833 Flag("b"), 834 Flag("c"), 835 }; 836 Flags f(flags); 837 EXPECT_EQ(f.size(), 3); 838 for (auto i = 0; i < f.size(); i++) { 839 EXPECT_EQ(f.begin()[i].name(), flags[i].name()); 840 } 841 842 flags.clear(); 843 Flags f2(flags); 844 EXPECT_EQ(f2.size(), 0); 845 EXPECT_EQ(f.size(), 3); 846 847 Val v = f; 848 EXPECT_TRUE(v.is_flags()); 849 Flags f3 = v.get_flags(); 850 EXPECT_EQ(f3.size(), 3); 851 EXPECT_EQ(f.size(), 3); 852 } 853 854 TEST(component, value_host_resource) { 855 static const uint32_t RESOURCE_TYPE = 100; 856 857 static const auto check = [](Store::Context cx, const Val &v, uint32_t rep) { 858 EXPECT_TRUE(v.is_resource()); 859 const ResourceAny &f = v.get_resource(); 860 EXPECT_EQ(f.type(), ResourceType(RESOURCE_TYPE)); 861 ResourceHost r = f.to_host(cx).unwrap(); 862 EXPECT_EQ(r.rep(), rep); 863 EXPECT_EQ(r.type(), RESOURCE_TYPE); 864 }; 865 866 static const auto make = [](Store::Context cx, uint32_t rep) -> Val { 867 return ResourceHost(true, rep, RESOURCE_TYPE).to_any(cx).unwrap(); 868 }; 869 870 Engine engine; 871 Linker linker(engine); 872 { 873 LinkerInstance i = linker.root(); 874 875 i.add_resource( 876 "r", ResourceType(RESOURCE_TYPE), 877 +[](Store::Context, uint32_t rep) -> Result<std::monostate> { 878 EXPECT_EQ(rep, 42); 879 return std::monostate(); 880 }) 881 .unwrap(); 882 883 i.add_func( 884 "f", 885 +[](Store::Context cx, const FuncType &_ty, Span<const Val> args, 886 Span<Val> rets) -> Result<std::monostate> { 887 EXPECT_EQ(args.size(), 1); 888 check(cx, args[0], 42); 889 890 EXPECT_EQ(rets.size(), 1); 891 rets[0] = make(cx, 43); 892 return std::monostate(); 893 }) 894 .unwrap(); 895 } 896 897 auto ctx = Context::New(engine, 898 R"( 899 (component 900 (import "r" (type $r (sub resource))) 901 (import "f" (func $f (param "a" (own $r)) (result (own $r)))) 902 (core func $f (canon lower (func $f))) 903 904 (core module $m 905 (import "" "f" (func $f (param i32) (result i32))) 906 907 (func (export "call") (param i32) (result i32) 908 local.get 0 909 call $f) 910 ) 911 912 (core instance $m (instantiate $m 913 (with "" (instance 914 (export "f" (func $f)) 915 )) 916 )) 917 918 (func (export "call") (param "a" (own $r)) (result (own $r)) 919 (canon lift (core func $m "call"))) 920 ) 921 )", 922 linker); 923 924 auto arg = make(ctx.context, 42); 925 auto res = Val(false); 926 927 ctx.func.call(ctx.context, Span<const Val>(&arg, 1), Span<Val>(&res, 1)) 928 .unwrap(); 929 ctx.func.post_return(ctx.context).unwrap(); 930 931 check(ctx.context, res, 43); 932 } 933 934 TEST(component, value_guest_resource) { 935 Engine engine; 936 Linker linker(engine); 937 Store store(engine); 938 939 Component c = Component::compile(engine, 940 R"( 941 (component 942 (core module $a 943 (global $g (mut i32) (i32.const 0)) 944 945 (func (export "dtor") (param i32) (global.set $g (local.get 0))) 946 (func (export "last-dtor-rep") (result i32) global.get $g) 947 ) 948 (core instance $a (instantiate $a)) 949 (type $r' (resource (rep i32) (dtor (func $a "dtor")))) 950 (export $r "r" (type $r')) 951 952 (core func $new (canon resource.new $r)) 953 (core func $drop (canon resource.drop $r)) 954 955 (core module $b 956 (import "" "new" (func $new (param i32) (result i32))) 957 (import "" "drop" (func $drop (param i32))) 958 959 (func (export "new") (param i32) (result i32) 960 local.get 0 961 call $new) 962 963 (func (export "rep") (param i32) (result i32) 964 local.get 0) 965 966 (func (export "drop") (param i32) 967 local.get 0 968 call $drop) 969 ) 970 (core instance $b (instantiate $b 971 (with "" (instance 972 (export "new" (func $new)) 973 (export "drop" (func $drop)) 974 )) 975 )) 976 977 (func (export "new") (param "x" u32) (result (own $r)) 978 (canon lift (core func $b "new"))) 979 (func (export "rep") (param "x" (borrow $r)) (result u32) 980 (canon lift (core func $b "rep"))) 981 (func (export "drop") (param "x" (own $r)) 982 (canon lift (core func $b "drop"))) 983 (func (export "last-dtor-rep") (result u32) 984 (canon lift (core func $a "last-dtor-rep"))) 985 986 ) 987 )") 988 .unwrap(); 989 990 auto instance = linker.instantiate(store, c).unwrap(); 991 auto new_index = *instance.get_export_index(store, nullptr, "new"); 992 auto rep_index = *instance.get_export_index(store, nullptr, "rep"); 993 auto drop_index = *instance.get_export_index(store, nullptr, "drop"); 994 auto last_dtor_rep_index = 995 *instance.get_export_index(store, nullptr, "last-dtor-rep"); 996 auto new_func = *instance.get_func(store, new_index); 997 auto rep_func = *instance.get_func(store, rep_index); 998 auto drop_func = *instance.get_func(store, drop_index); 999 auto last_dtor_rep_func = *instance.get_func(store, last_dtor_rep_index); 1000 1001 auto arg1 = Val(uint32_t(100)); 1002 auto res1 = Val(false); 1003 new_func.call(store, Span<const Val>(&arg1, 1), Span<Val>(&res1, 1)).unwrap(); 1004 new_func.post_return(store).unwrap(); 1005 1006 auto arg2 = Val(uint32_t(101)); 1007 auto res2 = Val(false); 1008 new_func.call(store, Span<const Val>(&arg2, 1), Span<Val>(&res2, 1)).unwrap(); 1009 new_func.post_return(store).unwrap(); 1010 1011 { 1012 EXPECT_TRUE(res1.is_resource()); 1013 EXPECT_TRUE(res2.is_resource()); 1014 const auto &resource1 = res1.get_resource(); 1015 const auto &resource2 = res2.get_resource(); 1016 EXPECT_NE(resource1.type(), ResourceType(100)); 1017 EXPECT_NE(resource2.type(), ResourceType(100)); 1018 EXPECT_EQ(resource1.type(), resource2.type()); 1019 EXPECT_FALSE(resource1.to_host(store)); 1020 EXPECT_FALSE(resource2.to_host(store)); 1021 EXPECT_TRUE(resource1.owned()); 1022 EXPECT_TRUE(resource2.owned()); 1023 arg1 = resource1; 1024 arg2 = resource2; 1025 } 1026 1027 rep_func.call(store, Span<const Val>(&arg1, 1), Span<Val>(&res1, 1)).unwrap(); 1028 rep_func.post_return(store).unwrap(); 1029 rep_func.call(store, Span<const Val>(&arg2, 1), Span<Val>(&res2, 1)).unwrap(); 1030 rep_func.post_return(store).unwrap(); 1031 1032 EXPECT_TRUE(res1.is_u32()); 1033 EXPECT_TRUE(res2.is_u32()); 1034 EXPECT_EQ(res1.get_u32(), 100); 1035 EXPECT_EQ(res2.get_u32(), 101); 1036 1037 Val last_rep(false); 1038 last_dtor_rep_func.call(store, Span<const Val>(), Span<Val>(&last_rep, 1)) 1039 .unwrap(); 1040 last_dtor_rep_func.post_return(store).unwrap(); 1041 EXPECT_EQ(last_rep.get_u32(), 0); 1042 1043 drop_func.call(store, Span<const Val>(&arg1, 1), Span<Val>()).unwrap(); 1044 drop_func.post_return(store).unwrap(); 1045 1046 last_dtor_rep_func.call(store, Span<const Val>(), Span<Val>(&last_rep, 1)) 1047 .unwrap(); 1048 last_dtor_rep_func.post_return(store).unwrap(); 1049 EXPECT_EQ(last_rep.get_u32(), 100); 1050 1051 arg2.get_resource().drop(store).unwrap(); 1052 1053 last_dtor_rep_func.call(store, Span<const Val>(), Span<Val>(&last_rep, 1)) 1054 .unwrap(); 1055 last_dtor_rep_func.post_return(store).unwrap(); 1056 EXPECT_EQ(last_rep.get_u32(), 101); 1057 } 1058 1059 TEST(component, resources) { 1060 ResourceHost r1(true, 1, 2); 1061 EXPECT_TRUE(r1.owned()); 1062 EXPECT_EQ(r1.rep(), 1); 1063 EXPECT_EQ(r1.type(), 2); 1064 1065 ResourceHost r2 = r1; 1066 EXPECT_TRUE(r1.owned()); 1067 EXPECT_EQ(r1.rep(), 1); 1068 EXPECT_EQ(r1.type(), 2); 1069 EXPECT_TRUE(r2.owned()); 1070 EXPECT_EQ(r2.rep(), 1); 1071 EXPECT_EQ(r2.type(), 2); 1072 1073 Engine engine; 1074 Store store(engine); 1075 ResourceAny r3 = r2.to_any(store).unwrap(); 1076 EXPECT_TRUE(r3.owned()); 1077 ResourceType t3 = r3.type(); 1078 EXPECT_EQ(t3, ResourceType(2)); 1079 1080 ResourceHost r4 = r3.to_host(store).unwrap(); 1081 EXPECT_TRUE(r4.owned()); 1082 EXPECT_EQ(r4.rep(), 1); 1083 EXPECT_EQ(r4.type(), 2); 1084 1085 EXPECT_FALSE(r3.drop(store)); 1086 EXPECT_TRUE(r4.to_any(store).unwrap().drop(store)); 1087 1088 Val v = r4.to_any(store).unwrap(); 1089 EXPECT_TRUE(v.is_resource()); 1090 const ResourceAny &r5 = v.get_resource(); 1091 EXPECT_TRUE(r5.owned()); 1092 EXPECT_EQ(r5.type(), ResourceType(2)); 1093 } 1094