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