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
echo_component(std::string_view type,std::string_view func,std::string_view host_params)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
NewContext69 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
create(std::string_view type,std::string_view body,std::string_view host_params,host_func_t callback)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
TEST(component,value_record)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
TEST(component,value_string)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
TEST(component,value_list)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
TEST(component,value_tuple)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
TEST(component,value_variant)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
TEST(component,value_enum)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
TEST(component,value_option)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
TEST(component,value_result)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
TEST(component,value_flags)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
TEST(component,value_list_inner)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
TEST(component,records)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
TEST(component,lists)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
TEST(component,tuples)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
TEST(component,variants)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
TEST(component,strings)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
TEST(component,results)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
TEST(component,enums)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
TEST(component,options)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
TEST(component,flags)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
TEST(component,value_host_resource)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
TEST(component,value_guest_resource)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
TEST(component,resources)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