1 /* 2 An example of how to interact with multiple memories. 3 4 You can compile and run this example on Linux with: 5 6 cargo build --release -p wasmtime-c-api 7 cc examples/multimemory.c \ 8 -I crates/c-api/include \ 9 -I crates/c-api/wasm-c-api/include \ 10 target/release/libwasmtime.a \ 11 -lpthread -ldl -lm \ 12 -o multimemory 13 ./multimemory 14 15 Note that on Windows and macOS the command will be similar, but you'll need 16 to tweak the `-lpthread` and such annotations. 17 18 You can also build using cmake: 19 20 mkdir build && cd build && cmake .. && \ 21 cmake --build . --target wasmtime-multimemory 22 */ 23 24 #include <inttypes.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <wasm.h> 29 #include <wasmtime.h> 30 31 static void exit_with_error(const char *message, wasmtime_error_t *error, 32 wasm_trap_t *trap); 33 34 void check(bool success) { 35 if (!success) { 36 printf("> Error, expected success\n"); 37 exit(1); 38 } 39 } 40 41 void check_call(wasmtime_context_t *store, wasmtime_func_t *func, 42 const wasmtime_val_t *args, size_t nargs, int32_t expected) { 43 wasmtime_val_t results[1]; 44 wasm_trap_t *trap = NULL; 45 wasmtime_error_t *error = 46 wasmtime_func_call(store, func, args, nargs, results, 1, &trap); 47 if (error != NULL || trap != NULL) 48 exit_with_error("failed to call function", error, trap); 49 if (results[0].of.i32 != expected) { 50 printf("> Error on result\n"); 51 exit(1); 52 } 53 } 54 55 void check_call0(wasmtime_context_t *store, wasmtime_func_t *func, 56 int32_t expected) { 57 check_call(store, func, NULL, 0, expected); 58 } 59 60 void check_call1(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg, 61 int32_t expected) { 62 wasmtime_val_t args[1]; 63 args[0].kind = WASMTIME_I32; 64 args[0].of.i32 = arg; 65 check_call(store, func, args, 1, expected); 66 } 67 68 void check_call2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, 69 int32_t arg2, int32_t expected) { 70 wasmtime_val_t args[2]; 71 args[0].kind = WASMTIME_I32; 72 args[0].of.i32 = arg1; 73 args[1].kind = WASMTIME_I32; 74 args[1].of.i32 = arg2; 75 check_call(store, func, args, 2, expected); 76 } 77 78 void check_ok(wasmtime_context_t *store, wasmtime_func_t *func, 79 const wasmtime_val_t *args, size_t nargs) { 80 wasm_trap_t *trap = NULL; 81 wasmtime_error_t *error = 82 wasmtime_func_call(store, func, args, nargs, NULL, 0, &trap); 83 if (error != NULL || trap != NULL) 84 exit_with_error("failed to call function", error, trap); 85 } 86 87 void check_ok2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, 88 int32_t arg2) { 89 wasmtime_val_t args[2]; 90 args[0].kind = WASMTIME_I32; 91 args[0].of.i32 = arg1; 92 args[1].kind = WASMTIME_I32; 93 args[1].of.i32 = arg2; 94 check_ok(store, func, args, 2); 95 } 96 97 void check_trap(wasmtime_context_t *store, wasmtime_func_t *func, 98 const wasmtime_val_t *args, size_t nargs, size_t num_results) { 99 assert(num_results <= 1); 100 wasmtime_val_t results[1]; 101 wasm_trap_t *trap = NULL; 102 wasmtime_error_t *error = 103 wasmtime_func_call(store, func, args, nargs, results, num_results, &trap); 104 if (error != NULL) 105 exit_with_error("failed to call function", error, NULL); 106 if (trap == NULL) { 107 printf("> Error on result, expected trap\n"); 108 exit(1); 109 } 110 wasm_trap_delete(trap); 111 } 112 113 void check_trap1(wasmtime_context_t *store, wasmtime_func_t *func, 114 int32_t arg) { 115 wasmtime_val_t args[1]; 116 args[0].kind = WASMTIME_I32; 117 args[0].of.i32 = arg; 118 check_trap(store, func, args, 1, 1); 119 } 120 121 void check_trap2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, 122 int32_t arg2) { 123 wasmtime_val_t args[2]; 124 args[0].kind = WASMTIME_I32; 125 args[0].of.i32 = arg1; 126 args[1].kind = WASMTIME_I32; 127 args[1].of.i32 = arg2; 128 check_trap(store, func, args, 2, 0); 129 } 130 131 int main(int argc, const char *argv[]) { 132 // Initialize. 133 printf("Initializing...\n"); 134 135 wasm_config_t *config = wasm_config_new(); 136 assert(config != NULL); 137 wasmtime_config_wasm_multi_memory_set(config, true); 138 139 wasm_engine_t *engine = wasm_engine_new_with_config(config); 140 assert(engine != NULL); 141 142 wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL); 143 wasmtime_context_t *context = wasmtime_store_context(store); 144 145 // Load our input file to parse it next 146 FILE *file = fopen("examples/multimemory.wat", "r"); 147 if (!file) { 148 printf("> Error loading file!\n"); 149 return 1; 150 } 151 fseek(file, 0L, SEEK_END); 152 size_t file_size = ftell(file); 153 fseek(file, 0L, SEEK_SET); 154 wasm_byte_vec_t wat; 155 wasm_byte_vec_new_uninitialized(&wat, file_size); 156 if (fread(wat.data, file_size, 1, file) != 1) { 157 printf("> Error loading module!\n"); 158 return 1; 159 } 160 fclose(file); 161 162 // Parse the wat into the binary wasm format 163 wasm_byte_vec_t binary; 164 wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &binary); 165 if (error != NULL) 166 exit_with_error("failed to parse wat", error, NULL); 167 wasm_byte_vec_delete(&wat); 168 169 // Compile. 170 printf("Compiling module...\n"); 171 wasmtime_module_t *module = NULL; 172 error = 173 wasmtime_module_new(engine, (uint8_t *)binary.data, binary.size, &module); 174 if (error) 175 exit_with_error("failed to compile module", error, NULL); 176 wasm_byte_vec_delete(&binary); 177 178 // Instantiate. 179 printf("Instantiating module...\n"); 180 wasmtime_instance_t instance; 181 wasm_trap_t *trap = NULL; 182 error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap); 183 if (error != NULL || trap != NULL) 184 exit_with_error("failed to instantiate", error, trap); 185 wasmtime_module_delete(module); 186 187 // Extract export. 188 printf("Extracting exports...\n"); 189 wasmtime_memory_t memory0, memory1; 190 wasmtime_func_t size0, load0, store0, size1, load1, store1; 191 wasmtime_extern_t item; 192 bool ok; 193 ok = wasmtime_instance_export_get(context, &instance, "memory0", 194 strlen("memory0"), &item); 195 assert(ok && item.kind == WASMTIME_EXTERN_MEMORY); 196 memory0 = item.of.memory; 197 ok = wasmtime_instance_export_get(context, &instance, "size0", 198 strlen("size0"), &item); 199 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 200 size0 = item.of.func; 201 ok = wasmtime_instance_export_get(context, &instance, "load0", 202 strlen("load0"), &item); 203 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 204 load0 = item.of.func; 205 ok = wasmtime_instance_export_get(context, &instance, "store0", 206 strlen("store0"), &item); 207 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 208 store0 = item.of.func; 209 ok = wasmtime_instance_export_get(context, &instance, "memory1", 210 strlen("memory1"), &item); 211 assert(ok && item.kind == WASMTIME_EXTERN_MEMORY); 212 memory1 = item.of.memory; 213 ok = wasmtime_instance_export_get(context, &instance, "size1", 214 strlen("size1"), &item); 215 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 216 size1 = item.of.func; 217 ok = wasmtime_instance_export_get(context, &instance, "load1", 218 strlen("load1"), &item); 219 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 220 load1 = item.of.func; 221 ok = wasmtime_instance_export_get(context, &instance, "store1", 222 strlen("store1"), &item); 223 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 224 store1 = item.of.func; 225 226 // Check initial memory. 227 printf("Checking memory...\n"); 228 check(wasmtime_memory_size(context, &memory0) == 2); 229 check(wasmtime_memory_data_size(context, &memory0) == 0x20000); 230 check(wasmtime_memory_data(context, &memory0)[0] == 0); 231 check(wasmtime_memory_data(context, &memory0)[0x1000] == 1); 232 check(wasmtime_memory_data(context, &memory0)[0x1001] == 2); 233 check(wasmtime_memory_data(context, &memory0)[0x1002] == 3); 234 check(wasmtime_memory_data(context, &memory0)[0x1003] == 4); 235 236 check_call0(context, &size0, 2); 237 check_call1(context, &load0, 0, 0); 238 check_call1(context, &load0, 0x1000, 1); 239 check_call1(context, &load0, 0x1001, 2); 240 check_call1(context, &load0, 0x1002, 3); 241 check_call1(context, &load0, 0x1003, 4); 242 check_call1(context, &load0, 0x1ffff, 0); 243 check_trap1(context, &load0, 0x20000); 244 245 check(wasmtime_memory_size(context, &memory1) == 2); 246 check(wasmtime_memory_data_size(context, &memory1) == 0x20000); 247 check(wasmtime_memory_data(context, &memory1)[0] == 0); 248 check(wasmtime_memory_data(context, &memory1)[0x1000] == 4); 249 check(wasmtime_memory_data(context, &memory1)[0x1001] == 3); 250 check(wasmtime_memory_data(context, &memory1)[0x1002] == 2); 251 check(wasmtime_memory_data(context, &memory1)[0x1003] == 1); 252 253 check_call0(context, &size1, 2); 254 check_call1(context, &load1, 0, 0); 255 check_call1(context, &load1, 0x1000, 4); 256 check_call1(context, &load1, 0x1001, 3); 257 check_call1(context, &load1, 0x1002, 2); 258 check_call1(context, &load1, 0x1003, 1); 259 check_call1(context, &load1, 0x1ffff, 0); 260 check_trap1(context, &load1, 0x20000); 261 262 // Mutate memory. 263 printf("Mutating memory...\n"); 264 wasmtime_memory_data(context, &memory0)[0x1003] = 5; 265 check_ok2(context, &store0, 0x1002, 6); 266 check_trap2(context, &store0, 0x20000, 0); 267 268 check(wasmtime_memory_data(context, &memory0)[0x1002] == 6); 269 check(wasmtime_memory_data(context, &memory0)[0x1003] == 5); 270 check_call1(context, &load0, 0x1002, 6); 271 check_call1(context, &load0, 0x1003, 5); 272 273 wasmtime_memory_data(context, &memory1)[0x1003] = 7; 274 check_ok2(context, &store1, 0x1002, 8); 275 check_trap2(context, &store1, 0x20000, 0); 276 277 check(wasmtime_memory_data(context, &memory1)[0x1002] == 8); 278 check(wasmtime_memory_data(context, &memory1)[0x1003] == 7); 279 check_call1(context, &load1, 0x1002, 8); 280 check_call1(context, &load1, 0x1003, 7); 281 282 // Grow memory. 283 printf("Growing memory...\n"); 284 uint64_t old_size; 285 error = wasmtime_memory_grow(context, &memory0, 1, &old_size); 286 if (error != NULL) 287 exit_with_error("failed to grow memory", error, trap); 288 check(wasmtime_memory_size(context, &memory0) == 3); 289 check(wasmtime_memory_data_size(context, &memory0) == 0x30000); 290 291 check_call1(context, &load0, 0x20000, 0); 292 check_ok2(context, &store0, 0x20000, 0); 293 check_trap1(context, &load0, 0x30000); 294 check_trap2(context, &store0, 0x30000, 0); 295 296 error = wasmtime_memory_grow(context, &memory0, 1, &old_size); 297 assert(error != NULL); 298 wasmtime_error_delete(error); 299 error = wasmtime_memory_grow(context, &memory0, 0, &old_size); 300 if (error != NULL) 301 exit_with_error("failed to grow memory", error, trap); 302 303 error = wasmtime_memory_grow(context, &memory1, 2, &old_size); 304 if (error != NULL) 305 exit_with_error("failed to grow memory", error, trap); 306 check(wasmtime_memory_size(context, &memory1) == 4); 307 check(wasmtime_memory_data_size(context, &memory1) == 0x40000); 308 309 check_call1(context, &load1, 0x30000, 0); 310 check_ok2(context, &store1, 0x30000, 0); 311 check_trap1(context, &load1, 0x40000); 312 check_trap2(context, &store1, 0x40000, 0); 313 314 error = wasmtime_memory_grow(context, &memory1, 1, &old_size); 315 assert(error != NULL); 316 wasmtime_error_delete(error); 317 error = wasmtime_memory_grow(context, &memory1, 0, &old_size); 318 if (error != NULL) 319 exit_with_error("failed to grow memory", error, trap); 320 321 // Shut down. 322 printf("Shutting down...\n"); 323 wasmtime_store_delete(store); 324 wasm_engine_delete(engine); 325 326 // All done. 327 printf("Done.\n"); 328 return 0; 329 } 330 331 static void exit_with_error(const char *message, wasmtime_error_t *error, 332 wasm_trap_t *trap) { 333 fprintf(stderr, "error: %s\n", message); 334 wasm_byte_vec_t error_message; 335 if (error != NULL) { 336 wasmtime_error_message(error, &error_message); 337 wasmtime_error_delete(error); 338 } else { 339 wasm_trap_message(trap, &error_message); 340 wasm_trap_delete(trap); 341 } 342 fprintf(stderr, "%.*s\n", (int)error_message.size, error_message.data); 343 wasm_byte_vec_delete(&error_message); 344 exit(1); 345 } 346