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