1 #include "utils.h"
2 
3 #include <gtest/gtest.h>
4 #include <wasmtime.h>
5 
6 #include <array>
7 #include <format>
8 #include <span>
9 
10 static std::string echo_component(std::string_view type, std::string_view func,
11                                   std::string_view host_params) {
12   return std::format(
13       R"END(
14 (component
15 	(type $Foo' {})
16 	(import "foo" (type $Foo (eq $Foo')))
17 	(import "do" (func $do (param "a" $Foo) (result $Foo)))
18 	(core module $libc
19 		(memory (export "memory") 1)
20 		{}
21 	)
22 	(core instance $libc (instantiate $libc))
23 	(core func $do_lower (canon lower (func $do) (memory $libc "memory") (realloc (func $libc "realloc"))))
24 
25 	(core module $doer
26 		(import "host" "do" (func $do (param {})))
27 		(import "libc" "memory" (memory 1))
28 		(import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
29 
30 		(func (export "call")
31 			{})
32 	)
33 	(core instance $doer (instantiate $doer
34 		(with "host" (instance (export "do" (func $do_lower))))
35 		(with "libc" (instance $libc))
36 	))
37 
38 	(func $call
39 		(param "a" $Foo)
40 		(result $Foo)
41 		(canon lift
42 			(core func $doer "call")
43 			(memory $libc "memory")
44 			(realloc (func $libc "realloc")))
45 	)
46 
47 	(export "call" (func $call))
48 )
49 		  )END",
50       type, REALLOC_AND_FREE, host_params, func);
51 }
52 
53 struct Context {
54   wasm_engine_t *engine;
55   wasmtime_store_t *store;
56   wasmtime_context_t *context;
57   wasmtime_component_t *component;
58   wasmtime_component_instance_t instance;
59   wasmtime_component_func_t func;
60 };
61 
62 static Context create(std::string_view type, std::string_view body,
63                       std::string_view host_params,
64                       wasmtime_component_func_callback_t callback) {
65   auto component_text = echo_component(type, body, host_params);
66   const auto engine = wasm_engine_new();
67   EXPECT_NE(engine, nullptr);
68 
69   const auto store = wasmtime_store_new(engine, nullptr, nullptr);
70   const auto context = wasmtime_store_context(store);
71 
72   wasmtime_component_t *component = nullptr;
73 
74   auto err = wasmtime_component_new(
75       engine, reinterpret_cast<const uint8_t *>(component_text.data()),
76       component_text.size(), &component);
77 
78   CHECK_ERR(err);
79 
80   auto f = wasmtime_component_get_export_index(component, nullptr, "call",
81                                                strlen("call"));
82 
83   EXPECT_NE(f, nullptr);
84 
85   const auto linker = wasmtime_component_linker_new(engine);
86   const auto root = wasmtime_component_linker_root(linker);
87 
88   wasmtime_component_linker_instance_add_func(root, "do", strlen("do"),
89                                               callback, nullptr, nullptr);
90 
91   wasmtime_component_linker_instance_delete(root);
92 
93   wasmtime_component_instance_t instance = {};
94   err = wasmtime_component_linker_instantiate(linker, context, component,
95                                               &instance);
96   CHECK_ERR(err);
97 
98   wasmtime_component_linker_delete(linker);
99 
100   wasmtime_component_func_t func = {};
101   const auto found =
102       wasmtime_component_instance_get_func(&instance, context, f, &func);
103   EXPECT_TRUE(found);
104   EXPECT_NE(func.store_id, 0);
105 
106   wasmtime_component_export_index_delete(f);
107 
108   return Context{
109       .engine = engine,
110       .store = store,
111       .context = context,
112       .component = component,
113       .instance = instance,
114       .func = func,
115   };
116 }
117 
118 static void destroy(Context &ctx) {
119   wasmtime_component_delete(ctx.component);
120   wasmtime_store_delete(ctx.store);
121   wasm_engine_delete(ctx.engine);
122 }
123 
124 TEST(component, value_record) {
125   static const auto check = [](const wasmtime_component_val_t &val, uint64_t x,
126                                uint64_t y) {
127     EXPECT_EQ(val.kind, WASMTIME_COMPONENT_RECORD);
128 
129     EXPECT_EQ(val.of.record.size, 2);
130     const auto entries = val.of.record.data;
131 
132     EXPECT_EQ((std::string_view{entries[0].name.data, entries[0].name.size}),
133               "x");
134     EXPECT_EQ(entries[0].val.kind, WASMTIME_COMPONENT_U64);
135     EXPECT_EQ(entries[0].val.of.u64, x);
136 
137     EXPECT_EQ((std::string_view{entries[1].name.data, entries[1].name.size}),
138               "y");
139     EXPECT_EQ(entries[1].val.kind, WASMTIME_COMPONENT_U64);
140     EXPECT_EQ(entries[1].val.of.u64, y);
141   };
142 
143   static const auto make = [](uint64_t x,
144                               uint64_t y) -> wasmtime_component_val_t {
145     auto ret = wasmtime_component_val_t{
146         .kind = WASMTIME_COMPONENT_RECORD,
147     };
148 
149     wasmtime_component_valrecord_new_uninit(&ret.of.record, 2);
150 
151     const auto entries = ret.of.record.data;
152     wasm_name_new_from_string(&entries[0].name, "x");
153     entries[0].val.kind = WASMTIME_COMPONENT_U64;
154     entries[0].val.of.u64 = x;
155     wasm_name_new_from_string(&entries[1].name, "y");
156     entries[1].val.kind = WASMTIME_COMPONENT_U64;
157     entries[1].val.of.u64 = y;
158 
159     return ret;
160   };
161 
162   auto ctx = create(
163       R"((record (field "x" u64) (field "y" u64)))", R"(
164 (param $x i64)
165 (param $y i64)
166 (result i32)
167 (local $res i32)
168 local.get $x
169 local.get $y
170 (call $realloc
171 	(i32.const 0)
172 	(i32.const 0)
173 	(i32.const 4)
174 	(i32.const 16))
175 local.tee $res
176 call $do
177 local.get $res
178 	  )",
179       "i64 i64 i32",
180       +[](void *, wasmtime_context_t *, const wasmtime_component_val_t *args,
181           size_t args_len, wasmtime_component_val_t *rets,
182           size_t rets_len) -> wasmtime_error_t * {
183         EXPECT_EQ(args_len, 1);
184         check(args[0], 1, 2);
185 
186         EXPECT_EQ(rets_len, 1);
187         rets[0] = make(3, 4);
188 
189         return nullptr;
190       });
191 
192   auto arg = make(1, 2);
193   auto res = wasmtime_component_val_t{};
194 
195   auto err =
196       wasmtime_component_func_call(&ctx.func, ctx.context, &arg, 1, &res, 1);
197   CHECK_ERR(err);
198 
199   check(res, 3, 4);
200 
201   wasmtime_component_val_delete(&arg);
202   wasmtime_component_val_delete(&res);
203 
204   destroy(ctx);
205 }
206 
207 TEST(component, value_string) {
208   static const auto check = [](const wasmtime_component_val_t &val,
209                                std::string_view text) {
210     EXPECT_EQ(val.kind, WASMTIME_COMPONENT_STRING);
211     EXPECT_EQ((std::string_view{val.of.string.data, val.of.string.size}), text);
212   };
213 
214   static const auto make =
215       [](std::string_view text) -> wasmtime_component_val_t {
216     auto str = wasm_name_t{};
217     wasm_name_new_from_string(&str, text.data());
218 
219     return wasmtime_component_val_t{
220         .kind = WASMTIME_COMPONENT_STRING,
221         .of = {.string = str},
222     };
223   };
224 
225   auto ctx = create(
226       R"(string)", R"(
227 (param $x i32)
228 (param $y i32)
229 (result i32)
230 (local $res i32)
231 local.get $x
232 local.get $y
233 (call $realloc
234 	(i32.const 0)
235 	(i32.const 0)
236 	(i32.const 4)
237 	(i32.const 8))
238 local.tee $res
239 call $do
240 local.get $res
241 	  )",
242       "i32 i32 i32",
243       +[](void *, wasmtime_context_t *, const wasmtime_component_val_t *args,
244           size_t args_len, wasmtime_component_val_t *rets,
245           size_t rets_len) -> wasmtime_error_t * {
246         EXPECT_EQ(args_len, 1);
247         check(args[0], "hello from A!");
248 
249         EXPECT_EQ(rets_len, 1);
250         rets[0] = make("hello from B!");
251 
252         return nullptr;
253       });
254 
255   auto arg = make("hello from A!");
256   auto res = wasmtime_component_val_t{};
257 
258   auto err =
259       wasmtime_component_func_call(&ctx.func, ctx.context, &arg, 1, &res, 1);
260   CHECK_ERR(err);
261 
262   check(res, "hello from B!");
263 
264   wasmtime_component_val_delete(&arg);
265   wasmtime_component_val_delete(&res);
266 
267   destroy(ctx);
268 }
269 
270 TEST(component, value_list) {
271   static const auto check = [](const wasmtime_component_val_t &val,
272                                std::vector<uint32_t> data) {
273     EXPECT_EQ(val.kind, WASMTIME_COMPONENT_LIST);
274     auto vals = std::span{val.of.list.data, val.of.list.size};
275     EXPECT_EQ(vals.size(), data.size());
276     for (auto i = 0; i < data.size(); i++) {
277       EXPECT_EQ(vals[i].kind, WASMTIME_COMPONENT_U32);
278       EXPECT_EQ(vals[i].of.u32, data[i]);
279     }
280   };
281 
282   static const auto make =
283       [](std::vector<uint32_t> data) -> wasmtime_component_val_t {
284     auto ret = wasmtime_component_val_t{
285         .kind = WASMTIME_COMPONENT_LIST,
286     };
287 
288     wasmtime_component_vallist_new_uninit(&ret.of.list, data.size());
289 
290     for (auto i = 0; i < data.size(); i++) {
291       ret.of.list.data[i] = wasmtime_component_val_t{
292           .kind = WASMTIME_COMPONENT_U32,
293           .of = {.u32 = data[i]},
294       };
295     }
296 
297     return ret;
298   };
299 
300   auto ctx = create(
301       R"((list u32))", R"(
302 (param $x i32)
303 (param $y i32)
304 (result i32)
305 (local $res i32)
306 local.get $x
307 local.get $y
308 (call $realloc
309 	(i32.const 0)
310 	(i32.const 0)
311 	(i32.const 4)
312 	(i32.const 8))
313 local.tee $res
314 call $do
315 local.get $res
316 	  )",
317       "i32 i32 i32",
318       +[](void *, wasmtime_context_t *, const wasmtime_component_val_t *args,
319           size_t args_len, wasmtime_component_val_t *rets,
320           size_t rets_len) -> wasmtime_error_t * {
321         EXPECT_EQ(args_len, 1);
322         check(args[0], {1, 2, 3});
323 
324         EXPECT_EQ(rets_len, 1);
325         rets[0] = make({4, 5, 6, 7});
326 
327         return nullptr;
328       });
329 
330   auto arg = make({1, 2, 3});
331   auto res = wasmtime_component_val_t{};
332 
333   auto err =
334       wasmtime_component_func_call(&ctx.func, ctx.context, &arg, 1, &res, 1);
335   CHECK_ERR(err);
336 
337   check(res, {4, 5, 6, 7});
338 
339   wasmtime_component_val_delete(&arg);
340   wasmtime_component_val_delete(&res);
341 
342   destroy(ctx);
343 }
344 
345 TEST(component, value_list_inner) {
346   {
347     auto x = wasmtime_component_val_t{
348         .kind = WASMTIME_COMPONENT_LIST,
349     };
350     wasmtime_component_vallist_new_empty(&x.of.list);
351     EXPECT_EQ(x.of.list.data, nullptr);
352     EXPECT_EQ(x.of.list.size, 0);
353 
354     wasmtime_component_vallist_new_uninit(&x.of.list, 1);
355     EXPECT_NE(x.of.list.data, nullptr);
356     EXPECT_EQ(x.of.list.size, 1);
357 
358     wasmtime_component_vallist_delete(&x.of.list);
359 
360     auto items = std::array{
361         wasmtime_component_val_t{
362             .kind = WASMTIME_COMPONENT_U32,
363             .of = {.u32 = 123},
364         },
365     };
366 
367     wasmtime_component_vallist_new(&x.of.list, items.size(), items.data());
368     EXPECT_NE(x.of.list.data, nullptr);
369     EXPECT_EQ(x.of.list.size, 1);
370 
371     EXPECT_EQ(x.of.list.data[0].kind, WASMTIME_COMPONENT_U32);
372     EXPECT_EQ(x.of.list.data[0].of.u32, 123);
373 
374     auto clone = wasmtime_component_val_t{
375         .kind = WASMTIME_COMPONENT_LIST,
376     };
377 
378     wasmtime_component_vallist_copy(&clone.of.list, &x.of.list);
379     wasmtime_component_vallist_delete(&x.of.list);
380 
381     EXPECT_NE(clone.of.list.data, nullptr);
382     EXPECT_EQ(clone.of.list.size, 1);
383 
384     EXPECT_EQ(clone.of.list.data[0].kind, WASMTIME_COMPONENT_U32);
385     EXPECT_EQ(clone.of.list.data[0].of.u32, 123);
386 
387     wasmtime_component_vallist_delete(&clone.of.list);
388   }
389 }
390