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